From 719aa419f7aac77f036388c68dd2c3cd721c84d2 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 00:09:46 +0200 Subject: [PATCH 01/12] Implement support for custom seed sources --- lib/migrations/common/MigrationsLoader.js | 36 +++++++ lib/migrations/migrate/MigrationGenerator.js | 2 +- lib/migrations/migrate/Migrator.js | 13 ++- ...er.js => migrator-configuration-merger.js} | 6 +- .../migrate/sources/fs-migrations.js | 33 +------ lib/migrations/seed/Seeder.js | 99 +++---------------- .../seed/seeder-configuration-merger.js | 57 +++++++++++ lib/migrations/seed/sources/fs-seeds.js | 66 +++++++++++++ lib/migrations/util/import-file.js | 1 - test/tape/migrate.js | 2 +- types/index.d.ts | 12 +++ 11 files changed, 200 insertions(+), 127 deletions(-) create mode 100644 lib/migrations/common/MigrationsLoader.js rename lib/migrations/migrate/{configuration-merger.js => migrator-configuration-merger.js} (91%) create mode 100644 lib/migrations/seed/seeder-configuration-merger.js create mode 100644 lib/migrations/seed/sources/fs-seeds.js diff --git a/lib/migrations/common/MigrationsLoader.js b/lib/migrations/common/MigrationsLoader.js new file mode 100644 index 0000000000..b89999f9aa --- /dev/null +++ b/lib/migrations/common/MigrationsLoader.js @@ -0,0 +1,36 @@ +const path = require('path'); +const DEFAULT_LOAD_EXTENSIONS = Object.freeze([ + '.co', + '.coffee', + '.eg', + '.iced', + '.js', + '.cjs', + '.litcoffee', + '.ls', + '.ts', +]); + +class AbstractMigrationsLoader { + constructor(migrationDirectories, sortDirsSeparately, loadExtensions) { + this.sortDirsSeparately = sortDirsSeparately; + + if (!Array.isArray(migrationDirectories)) { + migrationDirectories = [migrationDirectories]; + } + this.migrationsPaths = migrationDirectories; + this.loadExtensions = loadExtensions || DEFAULT_LOAD_EXTENSIONS; + } + + getFile(migrationsInfo) { + const absoluteDir = path.resolve(process.cwd(), migrationsInfo.directory); + const _path = path.join(absoluteDir, migrationsInfo.file); + const importFile = require('../util/import-file'); // late import + return importFile(_path); + } +} + +module.exports = { + DEFAULT_LOAD_EXTENSIONS, + AbstractMigrationsLoader, +}; diff --git a/lib/migrations/migrate/MigrationGenerator.js b/lib/migrations/migrate/MigrationGenerator.js index d9c084fb56..a33d55bd4f 100644 --- a/lib/migrations/migrate/MigrationGenerator.js +++ b/lib/migrations/migrate/MigrationGenerator.js @@ -1,6 +1,6 @@ const path = require('path'); const { writeJsFileUsingTemplate } = require('../util/template'); -const { getMergedConfig } = require('./configuration-merger'); +const { getMergedConfig } = require('./migrator-configuration-merger'); const { ensureDirectoryExists } = require('../util/fs'); const { yyyymmddhhmmss } = require('../util/timestamp'); diff --git a/lib/migrations/migrate/Migrator.js b/lib/migrations/migrate/Migrator.js index 1e484d7aff..353ea18f50 100644 --- a/lib/migrations/migrate/Migrator.js +++ b/lib/migrations/migrate/Migrator.js @@ -4,7 +4,6 @@ const differenceWith = require('lodash/differenceWith'); const get = require('lodash/get'); const isEmpty = require('lodash/isEmpty'); const max = require('lodash/max'); -const { inherits } = require('util'); const { getLockTableName, getTable, @@ -13,16 +12,16 @@ const { const { getSchemaBuilder } = require('./table-creator'); const migrationListResolver = require('./migration-list-resolver'); const MigrationGenerator = require('./MigrationGenerator'); -const { getMergedConfig } = require('./configuration-merger'); +const { getMergedConfig } = require('./migrator-configuration-merger'); const { isBoolean, isFunction } = require('../../util/is'); -function LockError(msg) { - this.name = 'MigrationLocked'; - this.message = msg; +class LockError extends Error { + constructor(msg) { + super(msg); + this.name = 'MigrationLocked'; + } } -inherits(LockError, Error); - // The new migration we're performing, typically called from the `knex.migrate` // interface on the main `knex` object. Passes the `knex` instance performing // the migration. diff --git a/lib/migrations/migrate/configuration-merger.js b/lib/migrations/migrate/migrator-configuration-merger.js similarity index 91% rename from lib/migrations/migrate/configuration-merger.js rename to lib/migrations/migrate/migrator-configuration-merger.js index 11dcd1f90e..aa65869332 100644 --- a/lib/migrations/migrate/configuration-merger.js +++ b/lib/migrations/migrate/migrator-configuration-merger.js @@ -1,8 +1,6 @@ -const { - FsMigrations, - DEFAULT_LOAD_EXTENSIONS, -} = require('./sources/fs-migrations'); +const { FsMigrations } = require('./sources/fs-migrations'); const Logger = require('../../logger'); +const { DEFAULT_LOAD_EXTENSIONS } = require('../common/MigrationsLoader'); const defaultLogger = new Logger(); const CONFIG_DEFAULT = Object.freeze({ diff --git a/lib/migrations/migrate/sources/fs-migrations.js b/lib/migrations/migrate/sources/fs-migrations.js index 2fdfdd8fce..a72b3242fb 100644 --- a/lib/migrations/migrate/sources/fs-migrations.js +++ b/lib/migrations/migrate/sources/fs-migrations.js @@ -2,30 +2,9 @@ const path = require('path'); const sortBy = require('lodash/sortBy'); const { readdir } = require('../../util/fs'); +const { AbstractMigrationsLoader } = require('../../common/MigrationsLoader'); -const DEFAULT_LOAD_EXTENSIONS = Object.freeze([ - '.co', - '.coffee', - '.eg', - '.iced', - '.js', - '.cjs', - '.litcoffee', - '.ls', - '.ts', -]); - -class FsMigrations { - constructor(migrationDirectories, sortDirsSeparately, loadExtensions) { - this.sortDirsSeparately = sortDirsSeparately; - - if (!Array.isArray(migrationDirectories)) { - migrationDirectories = [migrationDirectories]; - } - this.migrationsPaths = migrationDirectories; - this.loadExtensions = loadExtensions || DEFAULT_LOAD_EXTENSIONS; - } - +class FsMigrations extends AbstractMigrationsLoader { /** * Gets the migration names * @returns Promise @@ -77,11 +56,8 @@ class FsMigrations { return migration.file; } - getMigration(migration) { - const absoluteDir = path.resolve(process.cwd(), migration.directory); - const _path = path.join(absoluteDir, migration.file); - const importFile = require('../../util/import-file'); // late import - return importFile(_path); + getMigration(migrationInfo) { + return this.getFile(migrationInfo); } } @@ -94,6 +70,5 @@ function filterMigrations(migrationSource, migrations, loadExtensions) { } module.exports = { - DEFAULT_LOAD_EXTENSIONS, FsMigrations, }; diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index 55d00b2820..a46fbc7722 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -8,11 +8,7 @@ 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); - return includes(extensions, extension); -}; +const { getMergedConfig } = require('./seeder-configuration-merger'); // The new seeds we're performing, typically called from the `knex.seed` // interface on the main `knex` object. Passes the `knex` instance performing @@ -26,15 +22,11 @@ class Seeder { // Runs seed files for the given environment. async run(config) { this.config = this.setConfig(config); - let files = await this._listAll(); - if (config && config.specific) { - files = files.filter((file) => path.basename(file) === config.specific); - if (files.length === 0) { - throw new Error( - `Invalid argument provided: the specific seed "${config.specific}" does not exist.` - ); - } - } + const files = await this.config.seedSource.getSeeds( + this.config.loadExtensions, + this.config.recursive, + this.config.specific + ); return this._runSeeds(files); } @@ -48,45 +40,24 @@ class Seeder { return seedPath; } - // Lists all available seed files as a sorted array. - async _listAll(config) { - this.config = this.setConfig(config); - const { loadExtensions, recursive } = this.config; - const seeds = flatten( - await Promise.all( - this._absoluteConfigDirs().map((d) => - getFilepathsInFolder(d, recursive) - ) - ) - ); - // if true, each dir are already sorted - // (getFilepathsInFolderRecursively does this) - // if false, we need to sort all the seeds - if (this.config.sortDirsSeparately) { - return seeds.filter(filterByLoadExtensions(loadExtensions)); - } else { - return seeds.filter(filterByLoadExtensions(loadExtensions)).sort(); - } - } - // Ensures a folder for the seeds exist, dependent on the // seed config settings. _ensureFolder() { - const dirs = this._absoluteConfigDirs(); + const dirs = this.config.seedSource._getConfigDirectories(); const promises = dirs.map(ensureDirectoryExists); return Promise.all(promises); } // Run seed files, in sequence. - _runSeeds(seeds) { - seeds.forEach((seed) => this._validateSeedStructure(seed)); + async _runSeeds(seeds) { + for (const seed of seeds) { + await this._validateSeedStructure(seed); + } return this._waterfallBatch(seeds); } - // Validates seed files by requiring and checking for a `seed` function. async _validateSeedStructure(filepath) { - const importFile = require('../util/import-file'); // late import - const seed = await importFile(filepath); + const seed = await this.config.seedSource.getSeed(filepath); if (typeof seed.seed !== 'function') { throw new Error( `Invalid seed file: ${filepath} must have a seed function` @@ -114,7 +85,7 @@ class Seeder { _getNewStubFilePath(name) { const fileName = this._getNewStubFileName(name); - const dirs = this._absoluteConfigDirs(); + const dirs = this.config.seedSource._getConfigDirectories(); const dir = dirs.slice(-1)[0]; // Get last specified directory return path.join(dir, fileName); } @@ -137,8 +108,7 @@ class Seeder { const { knex } = this; const log = []; for (const seedPath of seeds) { - const importFile = require('../util/import-file'); // late import - const seed = await importFile(seedPath); + const seed = this.config.seedSource.getSeed(seedPath); try { await seed.seed(knex); log.push(seedPath); @@ -157,47 +127,8 @@ class Seeder { return [log]; } - /** - * Return all the config directories - * @returns {string[]} - */ - _absoluteConfigDirs() { - const directories = Array.isArray(this.config.directory) - ? this.config.directory - : [this.config.directory]; - return directories.map((directory) => { - if (!directory) { - console.warn( - 'Failed to resolve config file, knex cannot determine where to run or make seeds' - ); - } - return path.resolve(process.cwd(), directory); - }); - } - setConfig(config) { - return extend( - { - extension: 'js', - directory: './seeds', - loadExtensions: [ - '.co', - '.coffee', - '.eg', - '.iced', - '.js', - '.litcoffee', - '.ls', - '.ts', - '.cjs', - ], - timestampFilenamePrefix: false, - sortDirsSeparately: false, - recursive: false, - }, - this.config || {}, - config - ); + return getMergedConfig(config, undefined, this.knex.client.logger); } } diff --git a/lib/migrations/seed/seeder-configuration-merger.js b/lib/migrations/seed/seeder-configuration-merger.js new file mode 100644 index 0000000000..60c6bf2e73 --- /dev/null +++ b/lib/migrations/seed/seeder-configuration-merger.js @@ -0,0 +1,57 @@ +const { FsSeeds } = require('./sources/fs-seeds'); +const Logger = require('../../logger'); +const { DEFAULT_LOAD_EXTENSIONS } = require('../common/MigrationsLoader'); +const defaultLogger = new Logger(); + +const CONFIG_DEFAULT = Object.freeze({ + extension: 'js', + directory: './migrations', + loadExtensions: DEFAULT_LOAD_EXTENSIONS, + specific: null, + timestampFilenamePrefix: false, + recursive: false, + sortDirsSeparately: false, +}); + +function getMergedConfig(config, currentConfig, logger = defaultLogger) { + // config is the user specified config, mergedConfig has defaults and current config + // applied to it. + const mergedConfig = Object.assign( + {}, + CONFIG_DEFAULT, + currentConfig || {}, + config + ); + + if ( + config && + // If user specifies any FS related config, + // clear specified migrationSource to avoid ambiguity + (config.directory || + config.sortDirsSeparately !== undefined || + config.loadExtensions) + ) { + if (config.seedSource) { + logger.warn( + 'FS-related option specified for seed configuration. This resets seedSource to default FsMigrations' + ); + } + mergedConfig.seedSource = null; + } + + // If the user has not specified any configs, we need to + // default to fs migrations to maintain compatibility + if (!mergedConfig.seedSource) { + mergedConfig.seedSource = new FsSeeds( + mergedConfig.directory, + mergedConfig.sortDirsSeparately, + mergedConfig.loadExtensions + ); + } + + return mergedConfig; +} + +module.exports = { + getMergedConfig, +}; diff --git a/lib/migrations/seed/sources/fs-seeds.js b/lib/migrations/seed/sources/fs-seeds.js new file mode 100644 index 0000000000..3cb5129361 --- /dev/null +++ b/lib/migrations/seed/sources/fs-seeds.js @@ -0,0 +1,66 @@ +const path = require('path'); +const flatten = require('lodash/flatten'); +const { AbstractMigrationsLoader } = require('../../common/MigrationsLoader'); +const { getFilepathsInFolder } = require('../util/fs'); +const includes = require('lodash/includes'); +const importFile = require('../util/import-file'); + +const filterByLoadExtensions = (extensions) => (value) => { + const extension = path.extname(value); + return includes(extensions, extension); +}; + +class FsSeeds extends AbstractMigrationsLoader { + _getConfigDirectories() { + const directories = this.migrationsPaths; + return directories.map((directory) => { + if (!directory) { + console.warn( + 'Failed to resolve config file, knex cannot determine where to run or make seeds' + ); + } + return path.resolve(process.cwd(), directory); + }); + } + + async getSeeds(loadExtensions, recursive, runSpecificSeed) { + const seeds = flatten( + await Promise.all( + this._getConfigDirectories().map((d) => + getFilepathsInFolder(d, recursive) + ) + ) + ); + + let files; + // if true, each dir are already sorted + // (getFilepathsInFolderRecursively does this) + // if false, we need to sort all the seeds + if (this.sortDirsSeparately) { + files = seeds.filter(filterByLoadExtensions(loadExtensions)); + } else { + files = seeds.filter(filterByLoadExtensions(loadExtensions)).sort(); + } + + if (runSpecificSeed) { + files = files.filter((file) => path.basename(file) === runSpecificSeed); + if (files.length === 0) { + throw new Error( + `Invalid argument provided: the specific seed "${runSpecificSeed}" does not exist.` + ); + } + } + + return files; + } + + async getSeed(filepath) { + const importFile = require('../util/import-file'); // late import + const seed = await importFile(filepath); + return seed; + } +} + +module.exports = { + FsSeeds, +}; diff --git a/lib/migrations/util/import-file.js b/lib/migrations/util/import-file.js index 33a10b4464..3cbcac54c5 100644 --- a/lib/migrations/util/import-file.js +++ b/lib/migrations/util/import-file.js @@ -4,7 +4,6 @@ const isModuleType = require('./is-module-type'); * imports 'mjs', else requires. * NOTE: require me late! * @param {string} filepath - * @todo WARN on version 10 and '--experimental-modules' and '--esm' */ module.exports = async function importFile(filepath) { return (await isModuleType(filepath)) diff --git a/test/tape/migrate.js b/test/tape/migrate.js index 78076149f2..2d4ee2a6eb 100644 --- a/test/tape/migrate.js +++ b/test/tape/migrate.js @@ -3,7 +3,7 @@ const tape = require('tape'); const { Migrator } = require('../../lib/migrations/migrate/Migrator'); const mergeConfig = - require('../../lib/migrations/migrate/configuration-merger').getMergedConfig; + require('../../lib/migrations/migrate/migrator-configuration-merger').getMergedConfig; tape('migrate: constructor uses config.migrations', function (t) { t.plan(1); diff --git a/types/index.d.ts b/types/index.d.ts index a2172a32f6..d7eff4b7ef 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2605,6 +2605,17 @@ export declare namespace Knex { forceFreeMigrationsLock(config?: MigratorConfig): Promise; } + interface Seed { + seed: (knex: Knex) => PromiseLike; + } + + interface SeedSource { + getSeeds(loadExtensions: readonly string[], recursive?: boolean, runSpecificSeed?: boolean): Promise; + getSeedName(seed: TSeedSpec): string; + getSeed(seed: TSeedSpec): Seed; + validateSeedStructure(filepath: string): Promise + } + interface SeederConfig { extension?: string; directory?: string | readonly string[]; @@ -2615,6 +2626,7 @@ export declare namespace Knex { sortDirsSeparately?: boolean; stub?: string; variables?: V; + seedSource?: SeedSource; } class Seeder { From 341787ad80774171d33d98fe8c969a17791370cf Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 00:14:13 +0200 Subject: [PATCH 02/12] Fix linting and tests --- lib/migrations/seed/Seeder.js | 3 --- lib/migrations/seed/sources/fs-seeds.js | 5 ++--- types/index.d.ts | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index a46fbc7722..d42f7c6c95 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -2,9 +2,6 @@ // ------- const path = require('path'); -const flatten = require('lodash/flatten'); -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'); diff --git a/lib/migrations/seed/sources/fs-seeds.js b/lib/migrations/seed/sources/fs-seeds.js index 3cb5129361..b2ad1392cb 100644 --- a/lib/migrations/seed/sources/fs-seeds.js +++ b/lib/migrations/seed/sources/fs-seeds.js @@ -1,9 +1,8 @@ const path = require('path'); const flatten = require('lodash/flatten'); -const { AbstractMigrationsLoader } = require('../../common/MigrationsLoader'); -const { getFilepathsInFolder } = require('../util/fs'); const includes = require('lodash/includes'); -const importFile = require('../util/import-file'); +const { AbstractMigrationsLoader } = require('../../common/MigrationsLoader'); +const { getFilepathsInFolder } = require('../../util/fs'); const filterByLoadExtensions = (extensions) => (value) => { const extension = path.extname(value); diff --git a/types/index.d.ts b/types/index.d.ts index d7eff4b7ef..26239d7538 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2613,7 +2613,7 @@ export declare namespace Knex { getSeeds(loadExtensions: readonly string[], recursive?: boolean, runSpecificSeed?: boolean): Promise; getSeedName(seed: TSeedSpec): string; getSeed(seed: TSeedSpec): Seed; - validateSeedStructure(filepath: string): Promise + validateSeedStructure(filepath: string): Promise; } interface SeederConfig { From af430465d2919df77d83bd26947a69a476737052 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 00:17:21 +0200 Subject: [PATCH 03/12] Fix more tests and linting --- lib/migrations/seed/Seeder.js | 2 +- lib/migrations/seed/sources/fs-seeds.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index d42f7c6c95..aa9e5eeccd 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -2,7 +2,7 @@ // ------- const path = require('path'); -const { ensureDirectoryExists, getFilepathsInFolder } = require('../util/fs'); +const { ensureDirectoryExists } = require('../util/fs'); const { writeJsFileUsingTemplate } = require('../util/template'); const { yyyymmddhhmmss } = require('../util/timestamp'); const { getMergedConfig } = require('./seeder-configuration-merger'); diff --git a/lib/migrations/seed/sources/fs-seeds.js b/lib/migrations/seed/sources/fs-seeds.js index b2ad1392cb..bb7bcbe15e 100644 --- a/lib/migrations/seed/sources/fs-seeds.js +++ b/lib/migrations/seed/sources/fs-seeds.js @@ -54,7 +54,7 @@ class FsSeeds extends AbstractMigrationsLoader { } async getSeed(filepath) { - const importFile = require('../util/import-file'); // late import + const importFile = require('../../util/import-file'); // late import const seed = await importFile(filepath); return seed; } From f2869434f0538af61cac455075e4d57f853fb248 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 00:21:42 +0200 Subject: [PATCH 04/12] Fix seeding --- lib/migrations/seed/Seeder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index aa9e5eeccd..12ac85f7a8 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -105,7 +105,7 @@ class Seeder { const { knex } = this; const log = []; for (const seedPath of seeds) { - const seed = this.config.seedSource.getSeed(seedPath); + const seed = await this.config.seedSource.getSeed(seedPath); try { await seed.seed(knex); log.push(seedPath); From f73404415bd004459e1cf4058c93c7c4d9199b61 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 00:28:22 +0200 Subject: [PATCH 05/12] Fix more tests --- lib/migrations/seed/Seeder.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index 12ac85f7a8..827a36cc38 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -100,6 +100,15 @@ class Seeder { return seedPath; } + async _listAll(config) { + this.config = this.setConfig(config); + return this.config.seedSource.getSeeds( + this.config.loadExtensions, + this.config.recursive, + this.config.specific + ); + } + // Runs a batch of seed files. async _waterfallBatch(seeds) { const { knex } = this; @@ -125,7 +134,10 @@ class Seeder { } setConfig(config) { - return getMergedConfig(config, undefined, this.knex.client.logger); + if (config) { + return getMergedConfig(config, undefined, this.knex.client.logger); + } + return this.config; } } From 03f9969764f83405d3dafcbfd9de42e8e3e9d54c Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 00:41:39 +0200 Subject: [PATCH 06/12] Fix default directory --- lib/migrations/seed/Seeder.js | 10 +++++----- lib/migrations/seed/seeder-configuration-merger.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index 827a36cc38..09e0d08888 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -13,12 +13,12 @@ const { getMergedConfig } = require('./seeder-configuration-merger'); class Seeder { constructor(knex) { this.knex = knex; - this.config = this.setConfig(knex.client.config.seeds); + this.config = this.resolveConfig(knex.client.config.seeds); } // Runs seed files for the given environment. async run(config) { - this.config = this.setConfig(config); + this.config = this.resolveConfig(config); const files = await this.config.seedSource.getSeeds( this.config.loadExtensions, this.config.recursive, @@ -29,7 +29,7 @@ class Seeder { // Creates a new seed file, with a given name. async make(name, config) { - this.config = this.setConfig(config); + this.config = this.resolveConfig(config); if (!name) throw new Error('A name must be specified for the generated seed'); await this._ensureFolder(config); @@ -101,7 +101,7 @@ class Seeder { } async _listAll(config) { - this.config = this.setConfig(config); + this.config = this.resolveConfig(config); return this.config.seedSource.getSeeds( this.config.loadExtensions, this.config.recursive, @@ -133,7 +133,7 @@ class Seeder { return [log]; } - setConfig(config) { + resolveConfig(config) { if (config) { return getMergedConfig(config, undefined, this.knex.client.logger); } diff --git a/lib/migrations/seed/seeder-configuration-merger.js b/lib/migrations/seed/seeder-configuration-merger.js index 60c6bf2e73..c2dce61081 100644 --- a/lib/migrations/seed/seeder-configuration-merger.js +++ b/lib/migrations/seed/seeder-configuration-merger.js @@ -5,7 +5,7 @@ const defaultLogger = new Logger(); const CONFIG_DEFAULT = Object.freeze({ extension: 'js', - directory: './migrations', + directory: './seeds', loadExtensions: DEFAULT_LOAD_EXTENSIONS, specific: null, timestampFilenamePrefix: false, From dfd52fd55373cf53cff0d7e8635106be38db3116 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 00:59:50 +0200 Subject: [PATCH 07/12] Fix config merging --- lib/migrations/seed/Seeder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index 09e0d08888..d86e3e4587 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -135,7 +135,7 @@ class Seeder { resolveConfig(config) { if (config) { - return getMergedConfig(config, undefined, this.knex.client.logger); + return getMergedConfig(config, this.config, this.knex.client.logger); } return this.config; } From 8832cd61c06ffd5ff502eb4ed9aa56c8e49e239f Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 01:05:39 +0200 Subject: [PATCH 08/12] Always set config, even if it's just default --- lib/migrations/seed/Seeder.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index d86e3e4587..be1520b121 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -134,10 +134,7 @@ class Seeder { } resolveConfig(config) { - if (config) { - return getMergedConfig(config, this.config, this.knex.client.logger); - } - return this.config; + return getMergedConfig(config, this.config, this.knex.client.logger); } } From 94a00b8a3503e6742ee7706fdc6dc914a80bf007 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 23:58:49 +0200 Subject: [PATCH 09/12] Update types/index.d.ts Co-authored-by: maximelkin --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 26239d7538..11667c6495 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2612,7 +2612,7 @@ export declare namespace Knex { interface SeedSource { getSeeds(loadExtensions: readonly string[], recursive?: boolean, runSpecificSeed?: boolean): Promise; getSeedName(seed: TSeedSpec): string; - getSeed(seed: TSeedSpec): Seed; + getSeed(seed: TSeedSpec): Promise; validateSeedStructure(filepath: string): Promise; } From de7186b57be0b49cfd8721594713b73ab433ed2e Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Tue, 23 Nov 2021 23:58:55 +0200 Subject: [PATCH 10/12] Update types/index.d.ts Co-authored-by: maximelkin --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 11667c6495..085c50af56 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2606,7 +2606,7 @@ export declare namespace Knex { } interface Seed { - seed: (knex: Knex) => PromiseLike; + seed: (knex: Knex) => PromiseLike; } interface SeedSource { From bcc893839ba9d79035f9e6f49f771b90f14b22b0 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Wed, 24 Nov 2021 00:17:07 +0200 Subject: [PATCH 11/12] Address code review comments --- lib/migrations/seed/Seeder.js | 20 ++++++-------- .../seed/seeder-configuration-merger.js | 5 +++- lib/migrations/seed/sources/fs-seeds.js | 26 +++++++++---------- types/index.d.ts | 3 +-- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/lib/migrations/seed/Seeder.js b/lib/migrations/seed/Seeder.js index be1520b121..3c0c363e38 100644 --- a/lib/migrations/seed/Seeder.js +++ b/lib/migrations/seed/Seeder.js @@ -19,11 +19,7 @@ class Seeder { // Runs seed files for the given environment. async run(config) { this.config = this.resolveConfig(config); - const files = await this.config.seedSource.getSeeds( - this.config.loadExtensions, - this.config.recursive, - this.config.specific - ); + const files = await this.config.seedSource.getSeeds(this.config); return this._runSeeds(files); } @@ -40,7 +36,9 @@ class Seeder { // Ensures a folder for the seeds exist, dependent on the // seed config settings. _ensureFolder() { - const dirs = this.config.seedSource._getConfigDirectories(); + const dirs = this.config.seedSource._getConfigDirectories( + this.config.logger + ); const promises = dirs.map(ensureDirectoryExists); return Promise.all(promises); } @@ -82,7 +80,9 @@ class Seeder { _getNewStubFilePath(name) { const fileName = this._getNewStubFileName(name); - const dirs = this.config.seedSource._getConfigDirectories(); + const dirs = this.config.seedSource._getConfigDirectories( + this.config.logger + ); const dir = dirs.slice(-1)[0]; // Get last specified directory return path.join(dir, fileName); } @@ -102,11 +102,7 @@ class Seeder { async _listAll(config) { this.config = this.resolveConfig(config); - return this.config.seedSource.getSeeds( - this.config.loadExtensions, - this.config.recursive, - this.config.specific - ); + return this.config.seedSource.getSeeds(this.config); } // Runs a batch of seed files. diff --git a/lib/migrations/seed/seeder-configuration-merger.js b/lib/migrations/seed/seeder-configuration-merger.js index c2dce61081..1ff4a6b619 100644 --- a/lib/migrations/seed/seeder-configuration-merger.js +++ b/lib/migrations/seed/seeder-configuration-merger.js @@ -20,7 +20,10 @@ function getMergedConfig(config, currentConfig, logger = defaultLogger) { {}, CONFIG_DEFAULT, currentConfig || {}, - config + config, + { + logger, + } ); if ( diff --git a/lib/migrations/seed/sources/fs-seeds.js b/lib/migrations/seed/sources/fs-seeds.js index bb7bcbe15e..912536e750 100644 --- a/lib/migrations/seed/sources/fs-seeds.js +++ b/lib/migrations/seed/sources/fs-seeds.js @@ -10,42 +10,42 @@ const filterByLoadExtensions = (extensions) => (value) => { }; class FsSeeds extends AbstractMigrationsLoader { - _getConfigDirectories() { + _getConfigDirectories(logger) { const directories = this.migrationsPaths; return directories.map((directory) => { if (!directory) { - console.warn( - 'Failed to resolve config file, knex cannot determine where to run or make seeds' + logger.warn( + 'Empty value passed as a directory for Seeder, this is not supported.' ); } return path.resolve(process.cwd(), directory); }); } - async getSeeds(loadExtensions, recursive, runSpecificSeed) { + async getSeeds(config) { + const { loadExtensions, recursive, specific } = config; + const seeds = flatten( await Promise.all( - this._getConfigDirectories().map((d) => + this._getConfigDirectories(config.logger).map((d) => getFilepathsInFolder(d, recursive) ) ) ); - let files; // if true, each dir are already sorted // (getFilepathsInFolderRecursively does this) // if false, we need to sort all the seeds - if (this.sortDirsSeparately) { - files = seeds.filter(filterByLoadExtensions(loadExtensions)); - } else { - files = seeds.filter(filterByLoadExtensions(loadExtensions)).sort(); + let files = seeds.filter(filterByLoadExtensions(loadExtensions)); + if (!this.sortDirsSeparately) { + files.sort(); } - if (runSpecificSeed) { - files = files.filter((file) => path.basename(file) === runSpecificSeed); + if (specific) { + files = files.filter((file) => path.basename(file) === specific); if (files.length === 0) { throw new Error( - `Invalid argument provided: the specific seed "${runSpecificSeed}" does not exist.` + `Invalid argument provided: the specific seed "${specific}" does not exist.` ); } } diff --git a/types/index.d.ts b/types/index.d.ts index 085c50af56..11e2db3476 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2610,8 +2610,7 @@ export declare namespace Knex { } interface SeedSource { - getSeeds(loadExtensions: readonly string[], recursive?: boolean, runSpecificSeed?: boolean): Promise; - getSeedName(seed: TSeedSpec): string; + getSeeds(config: SeederConfig): Promise; getSeed(seed: TSeedSpec): Promise; validateSeedStructure(filepath: string): Promise; } From daede07484c2e93468ea2a8f56b09143994e6165 Mon Sep 17 00:00:00 2001 From: Igor Savin Date: Fri, 26 Nov 2021 23:06:51 +0200 Subject: [PATCH 12/12] Remove unneeded method --- types/index.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 11e2db3476..ef6617b501 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -2612,7 +2612,6 @@ export declare namespace Knex { interface SeedSource { getSeeds(config: SeederConfig): Promise; getSeed(seed: TSeedSpec): Promise; - validateSeedStructure(filepath: string): Promise; } interface SeederConfig {