2016-03-02 17:07:05 +01:00
|
|
|
// Seeder
|
|
|
|
// -------
|
|
|
|
|
2019-06-04 00:37:17 +02:00
|
|
|
const path = require('path');
|
2020-08-08 14:21:43 +02:00
|
|
|
const flatten = require('lodash/flatten');
|
2020-04-18 20:41:23 +03:00
|
|
|
const extend = require('lodash/extend');
|
|
|
|
const includes = require('lodash/includes');
|
2020-08-10 15:10:37 +02:00
|
|
|
const { ensureDirectoryExists, getFilepathsInFolder } = require('../util/fs');
|
2019-10-13 16:49:53 +05:30
|
|
|
const { writeJsFileUsingTemplate } = require('../util/template');
|
2019-06-04 00:37:17 +02:00
|
|
|
|
2020-08-08 14:21:43 +02:00
|
|
|
const filterByLoadExtensions = (extensions) => (value) => {
|
|
|
|
const extension = path.extname(value);
|
|
|
|
return includes(extensions, extension);
|
|
|
|
};
|
|
|
|
|
2019-06-26 01:31:23 -05:00
|
|
|
// The new seeds we're performing, typically called from the `knex.seed`
|
2016-03-02 17:07:05 +01:00
|
|
|
// interface on the main `knex` object. Passes the `knex` instance performing
|
|
|
|
// the seeds.
|
2019-10-13 16:49:53 +05:30
|
|
|
class Seeder {
|
|
|
|
constructor(knex) {
|
|
|
|
this.knex = knex;
|
|
|
|
this.config = this.setConfig(knex.client.config.seeds);
|
|
|
|
}
|
2016-03-02 17:07:05 +01:00
|
|
|
|
2019-10-13 16:49:53 +05:30
|
|
|
// Runs seed files for the given environment.
|
|
|
|
async run(config) {
|
|
|
|
this.config = this.setConfig(config);
|
2020-09-04 08:48:18 +02:00
|
|
|
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.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-10-13 16:49:53 +05:30
|
|
|
return this._runSeeds(files);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a new seed file, with a given name.
|
|
|
|
async make(name, config) {
|
|
|
|
this.config = this.setConfig(config);
|
|
|
|
if (!name)
|
|
|
|
throw new Error('A name must be specified for the generated seed');
|
|
|
|
await this._ensureFolder(config);
|
|
|
|
const seedPath = await this._writeNewSeed(name);
|
|
|
|
return seedPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lists all available seed files as a sorted array.
|
|
|
|
async _listAll(config) {
|
|
|
|
this.config = this.setConfig(config);
|
2020-08-10 15:10:37 +02:00
|
|
|
const { loadExtensions, recursive } = this.config;
|
2020-08-08 14:21:43 +02:00
|
|
|
const seeds = flatten(
|
|
|
|
await Promise.all(
|
2020-08-10 15:10:37 +02:00
|
|
|
this._absoluteConfigDirs().map((d) =>
|
|
|
|
getFilepathsInFolder(d, recursive)
|
|
|
|
)
|
2020-08-08 14:21:43 +02:00
|
|
|
)
|
2019-10-25 20:17:26 +02:00
|
|
|
);
|
2020-08-08 14:21:43 +02:00
|
|
|
// 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();
|
|
|
|
}
|
2019-10-13 16:49:53 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
// Ensures a folder for the seeds exist, dependent on the
|
|
|
|
// seed config settings.
|
2020-08-08 14:21:43 +02:00
|
|
|
_ensureFolder() {
|
|
|
|
const dirs = this._absoluteConfigDirs();
|
|
|
|
const promises = dirs.map(ensureDirectoryExists);
|
|
|
|
return Promise.all(promises);
|
2019-10-13 16:49:53 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
// Run seed files, in sequence.
|
|
|
|
_runSeeds(seeds) {
|
2019-10-16 00:03:35 +03:00
|
|
|
seeds.forEach((seed) => this._validateSeedStructure(seed));
|
|
|
|
return this._waterfallBatch(seeds);
|
2019-10-13 16:49:53 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
// Validates seed files by requiring and checking for a `seed` function.
|
2020-08-15 16:54:43 +02:00
|
|
|
async _validateSeedStructure(filepath) {
|
|
|
|
const importFile = require('../util/import-file'); // late import
|
|
|
|
const seed = await importFile(filepath);
|
2019-10-13 16:49:53 +05:30
|
|
|
if (typeof seed.seed !== 'function') {
|
2020-08-08 14:21:43 +02:00
|
|
|
throw new Error(
|
|
|
|
`Invalid seed file: ${filepath} must have a seed function`
|
|
|
|
);
|
2019-10-13 16:49:53 +05:30
|
|
|
}
|
2020-08-08 14:21:43 +02:00
|
|
|
return filepath;
|
2019-10-13 16:49:53 +05:30
|
|
|
}
|
2016-03-02 17:07:05 +01:00
|
|
|
|
2019-10-13 16:49:53 +05:30
|
|
|
_getStubPath() {
|
|
|
|
return (
|
|
|
|
this.config.stub ||
|
|
|
|
path.join(__dirname, 'stub', this.config.extension + '.stub')
|
|
|
|
);
|
2016-03-02 17:07:05 +01:00
|
|
|
}
|
2019-10-13 16:49:53 +05:30
|
|
|
|
|
|
|
_getNewStubFileName(name) {
|
2016-03-02 17:07:05 +01:00
|
|
|
if (name[0] === '-') name = name.slice(1);
|
2019-10-13 16:49:53 +05:30
|
|
|
return name + '.' + this.config.extension;
|
|
|
|
}
|
|
|
|
|
|
|
|
_getNewStubFilePath(name) {
|
2020-08-08 14:21:43 +02:00
|
|
|
const fileName = this._getNewStubFileName(name);
|
|
|
|
const dirs = this._absoluteConfigDirs();
|
|
|
|
const dir = dirs.slice(-1)[0]; // Get last specified directory
|
|
|
|
return path.join(dir, fileName);
|
2019-10-15 10:11:59 +03:00
|
|
|
}
|
2019-10-13 16:49:53 +05:30
|
|
|
|
|
|
|
// Write a new seed to disk, using the config and generated filename,
|
|
|
|
// passing any `variables` given in the config to the template.
|
|
|
|
async _writeNewSeed(name) {
|
|
|
|
const seedPath = this._getNewStubFilePath(name);
|
|
|
|
await writeJsFileUsingTemplate(
|
|
|
|
seedPath,
|
|
|
|
this._getStubPath(),
|
|
|
|
{ variable: 'd' },
|
|
|
|
this.config.variables || {}
|
|
|
|
);
|
|
|
|
return seedPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Runs a batch of seed files.
|
2019-10-16 00:03:35 +03:00
|
|
|
async _waterfallBatch(seeds) {
|
2019-10-13 16:49:53 +05:30
|
|
|
const { knex } = this;
|
|
|
|
const log = [];
|
2020-08-08 14:21:43 +02:00
|
|
|
for (const seedPath of seeds) {
|
2020-08-15 16:54:43 +02:00
|
|
|
const importFile = require('../util/import-file'); // late import
|
|
|
|
const seed = await importFile(seedPath);
|
2019-10-16 00:03:35 +03:00
|
|
|
try {
|
|
|
|
await seed.seed(knex);
|
|
|
|
log.push(seedPath);
|
|
|
|
} catch (originalError) {
|
|
|
|
const error = new Error(
|
|
|
|
`Error while executing "${seedPath}" seed: ${originalError.message}`
|
|
|
|
);
|
|
|
|
error.original = originalError;
|
|
|
|
error.stack =
|
2020-04-19 00:40:23 +02:00
|
|
|
error.stack.split('\n').slice(0, 2).join('\n') +
|
2019-10-16 00:03:35 +03:00
|
|
|
'\n' +
|
|
|
|
originalError.stack;
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [log];
|
2019-10-13 16:49:53 +05:30
|
|
|
}
|
|
|
|
|
2020-08-08 14:21:43 +02:00
|
|
|
/**
|
|
|
|
* 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) {
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console.warn(
|
|
|
|
'Failed to resolve config file, knex cannot determine where to run or make seeds'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return path.resolve(process.cwd(), directory);
|
|
|
|
});
|
2019-10-13 16:49:53 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
setConfig(config) {
|
|
|
|
return extend(
|
|
|
|
{
|
|
|
|
extension: 'js',
|
|
|
|
directory: './seeds',
|
|
|
|
loadExtensions: [
|
|
|
|
'.co',
|
|
|
|
'.coffee',
|
|
|
|
'.eg',
|
|
|
|
'.iced',
|
|
|
|
'.js',
|
|
|
|
'.litcoffee',
|
|
|
|
'.ls',
|
|
|
|
'.ts',
|
|
|
|
],
|
2020-08-08 14:21:43 +02:00
|
|
|
sortDirsSeparately: false,
|
2020-08-10 15:10:37 +02:00
|
|
|
recursive: false,
|
2019-10-13 16:49:53 +05:30
|
|
|
},
|
|
|
|
this.config || {},
|
|
|
|
config
|
2019-06-03 18:10:23 +02:00
|
|
|
);
|
2019-10-13 16:49:53 +05:30
|
|
|
}
|
|
|
|
}
|
2016-03-02 17:07:05 +01:00
|
|
|
|
2019-06-04 00:37:17 +02:00
|
|
|
module.exports = Seeder;
|