2016-03-02 17:07:05 +01:00
|
|
|
// Seeder
|
|
|
|
// -------
|
|
|
|
|
2019-06-04 00:37:17 +02:00
|
|
|
const fs = require('fs');
|
|
|
|
const path = require('path');
|
|
|
|
const mkdirp = require('mkdirp');
|
2019-06-17 02:14:17 +02:00
|
|
|
const Bluebird = require('bluebird');
|
2019-06-04 00:37:17 +02:00
|
|
|
const {
|
|
|
|
filter,
|
|
|
|
includes,
|
|
|
|
map,
|
|
|
|
bind,
|
|
|
|
template,
|
|
|
|
each,
|
|
|
|
extend,
|
|
|
|
} = require('lodash');
|
|
|
|
|
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.
|
|
|
|
function Seeder(knex) {
|
2016-05-18 19:59:24 +10:00
|
|
|
this.knex = knex;
|
2016-03-02 17:07:05 +01:00
|
|
|
this.config = this.setConfig(knex.client.config.seeds);
|
|
|
|
}
|
|
|
|
|
2019-07-07 12:11:39 +03:00
|
|
|
// Runs seed files for the given environment.
|
2019-06-18 00:07:24 +02:00
|
|
|
Seeder.prototype.run = async function(config) {
|
2016-03-02 17:07:05 +01:00
|
|
|
this.config = this.setConfig(config);
|
|
|
|
return this._seedData()
|
|
|
|
.bind(this)
|
2019-06-17 02:14:17 +02:00
|
|
|
.then(([all]) => {
|
2019-07-07 12:11:39 +03:00
|
|
|
const files =
|
|
|
|
config && config.specific
|
|
|
|
? all.filter((file) => file === config.specific)
|
|
|
|
: all;
|
|
|
|
|
|
|
|
return this._runSeeds(files);
|
2016-03-02 17:07:05 +01:00
|
|
|
});
|
2019-06-18 00:07:24 +02:00
|
|
|
};
|
2016-03-02 17:07:05 +01:00
|
|
|
|
|
|
|
// Creates a new seed file, with a given name.
|
|
|
|
Seeder.prototype.make = function(name, config) {
|
|
|
|
this.config = this.setConfig(config);
|
2018-07-09 08:10:34 -04:00
|
|
|
if (!name)
|
2019-06-17 02:14:17 +02:00
|
|
|
Bluebird.rejected(
|
2018-07-09 08:10:34 -04:00
|
|
|
new Error('A name must be specified for the generated seed')
|
|
|
|
);
|
2016-03-02 17:07:05 +01:00
|
|
|
return this._ensureFolder(config)
|
|
|
|
.bind(this)
|
|
|
|
.then(this._generateStubTemplate)
|
|
|
|
.then(this._writeNewSeed(name));
|
|
|
|
};
|
|
|
|
|
|
|
|
// Lists all available seed files as a sorted array.
|
2019-06-18 00:07:24 +02:00
|
|
|
Seeder.prototype._listAll = async function(config) {
|
2016-03-02 17:07:05 +01:00
|
|
|
this.config = this.setConfig(config);
|
2017-07-24 14:19:17 +03:00
|
|
|
const loadExtensions = this.config.loadExtensions;
|
2019-06-17 02:14:17 +02:00
|
|
|
return Bluebird.promisify(fs.readdir, { context: fs })(
|
2018-07-09 08:10:34 -04:00
|
|
|
this._absoluteConfigDir()
|
|
|
|
)
|
2016-03-02 17:07:05 +01:00
|
|
|
.bind(this)
|
2018-07-09 08:10:34 -04:00
|
|
|
.then((seeds) =>
|
2016-05-17 01:01:34 +10:00
|
|
|
filter(seeds, function(value) {
|
|
|
|
const extension = path.extname(value);
|
2017-07-24 14:19:17 +03:00
|
|
|
return includes(loadExtensions, extension);
|
2016-05-17 01:01:34 +10:00
|
|
|
}).sort()
|
|
|
|
);
|
2019-06-18 00:07:24 +02:00
|
|
|
};
|
2016-03-02 17:07:05 +01:00
|
|
|
|
2019-06-26 01:31:23 -05:00
|
|
|
// Gets the seed file list from the specified seed directory.
|
2016-03-02 17:07:05 +01:00
|
|
|
Seeder.prototype._seedData = function() {
|
2019-06-17 02:14:17 +02:00
|
|
|
return Bluebird.join(this._listAll());
|
2016-03-02 17:07:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// Ensures a folder for the seeds exist, dependent on the
|
|
|
|
// seed config settings.
|
|
|
|
Seeder.prototype._ensureFolder = function() {
|
2016-05-17 01:01:34 +10:00
|
|
|
const dir = this._absoluteConfigDir();
|
2019-06-17 02:14:17 +02:00
|
|
|
return Bluebird.promisify(fs.stat, { context: fs })(dir).catch(() =>
|
|
|
|
Bluebird.promisify(mkdirp)(dir)
|
2018-07-09 08:10:34 -04:00
|
|
|
);
|
2016-03-02 17:07:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// Run seed files, in sequence.
|
|
|
|
Seeder.prototype._runSeeds = function(seeds) {
|
2019-06-17 02:14:17 +02:00
|
|
|
return Bluebird.all(map(seeds, bind(this._validateSeedStructure, this)))
|
2016-03-02 17:07:05 +01:00
|
|
|
.bind(this)
|
|
|
|
.then(function(seeds) {
|
2019-06-17 02:14:17 +02:00
|
|
|
return Bluebird.bind(this).then(function() {
|
2018-07-09 08:10:34 -04:00
|
|
|
return this._waterfallBatch(seeds);
|
|
|
|
});
|
2016-03-02 17:07:05 +01:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
// Validates seed files by requiring and checking for a `seed` function.
|
|
|
|
Seeder.prototype._validateSeedStructure = function(name) {
|
2016-05-17 01:01:34 +10:00
|
|
|
const seed = require(path.join(this._absoluteConfigDir(), name));
|
2016-03-02 17:07:05 +01:00
|
|
|
if (typeof seed.seed !== 'function') {
|
2016-05-17 01:01:34 +10:00
|
|
|
throw new Error(`Invalid seed file: ${name} must have a seed function`);
|
2016-03-02 17:07:05 +01:00
|
|
|
}
|
|
|
|
return name;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Generates the stub template for the current seed file, returning a compiled template.
|
|
|
|
Seeder.prototype._generateStubTemplate = function() {
|
2018-07-09 08:10:34 -04:00
|
|
|
const stubPath =
|
|
|
|
this.config.stub ||
|
2016-05-17 01:01:34 +10:00
|
|
|
path.join(__dirname, 'stub', this.config.extension + '.stub');
|
2019-06-17 02:14:17 +02:00
|
|
|
return Bluebird.promisify(fs.readFile, { context: fs })(stubPath).then(
|
2018-07-09 08:10:34 -04:00
|
|
|
(stub) => template(stub.toString(), { variable: 'd' })
|
2016-05-17 01:01:34 +10:00
|
|
|
);
|
2016-03-02 17:07:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// Write a new seed to disk, using the config and generated filename,
|
|
|
|
// passing any `variables` given in the config to the template.
|
|
|
|
Seeder.prototype._writeNewSeed = function(name) {
|
2016-05-17 01:01:34 +10:00
|
|
|
const { config } = this;
|
|
|
|
const dir = this._absoluteConfigDir();
|
2016-03-02 17:07:05 +01:00
|
|
|
return function(tmpl) {
|
|
|
|
if (name[0] === '-') name = name.slice(1);
|
2016-05-18 19:59:24 +10:00
|
|
|
const filename = name + '.' + config.extension;
|
2019-06-17 02:14:17 +02:00
|
|
|
return Bluebird.promisify(fs.writeFile, { context: fs })(
|
2016-03-02 17:07:05 +01:00
|
|
|
path.join(dir, filename),
|
|
|
|
tmpl(config.variables || {})
|
|
|
|
).return(path.join(dir, filename));
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
// Runs a batch of seed files.
|
|
|
|
Seeder.prototype._waterfallBatch = function(seeds) {
|
2016-05-18 19:59:24 +10:00
|
|
|
const { knex } = this;
|
2016-05-17 01:01:34 +10:00
|
|
|
const seedDirectory = this._absoluteConfigDir();
|
2019-06-17 02:14:17 +02:00
|
|
|
let current = Bluebird.bind({ failed: false, failedOn: 0 });
|
2016-05-18 19:59:24 +10:00
|
|
|
const log = [];
|
2018-11-16 13:23:22 +01:00
|
|
|
each(seeds, (seed) => {
|
2016-05-18 19:59:24 +10:00
|
|
|
const name = path.join(seedDirectory, seed);
|
2016-03-02 17:07:05 +01:00
|
|
|
seed = require(name);
|
|
|
|
|
|
|
|
// Run each seed file.
|
2019-06-03 18:10:23 +02:00
|
|
|
current = current.then(() =>
|
|
|
|
// Nesting promise to prevent bubbling up of error on catch
|
|
|
|
Promise.resolve()
|
2019-06-08 01:16:34 +02:00
|
|
|
.then(() => seed.seed(knex))
|
2019-06-03 18:10:23 +02:00
|
|
|
.then(() => log.push(name))
|
|
|
|
.catch((originalError) => {
|
|
|
|
const error = new Error(
|
|
|
|
`Error while executing "${name}" seed: ${originalError.message}`
|
|
|
|
);
|
|
|
|
error.original = originalError;
|
|
|
|
error.stack =
|
|
|
|
error.stack
|
|
|
|
.split('\n')
|
|
|
|
.slice(0, 2)
|
|
|
|
.join('\n') +
|
|
|
|
'\n' +
|
|
|
|
originalError.stack;
|
|
|
|
throw error;
|
|
|
|
})
|
|
|
|
);
|
2016-03-02 17:07:05 +01:00
|
|
|
});
|
|
|
|
|
2019-06-17 02:14:17 +02:00
|
|
|
return current.then(() => [log]);
|
2016-03-02 17:07:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
Seeder.prototype._absoluteConfigDir = function() {
|
|
|
|
return path.resolve(process.cwd(), this.config.directory);
|
|
|
|
};
|
|
|
|
|
|
|
|
Seeder.prototype.setConfig = function(config) {
|
2018-07-09 08:10:34 -04:00
|
|
|
return extend(
|
|
|
|
{
|
|
|
|
extension: 'js',
|
|
|
|
directory: './seeds',
|
|
|
|
loadExtensions: [
|
|
|
|
'.co',
|
|
|
|
'.coffee',
|
|
|
|
'.eg',
|
|
|
|
'.iced',
|
|
|
|
'.js',
|
|
|
|
'.litcoffee',
|
|
|
|
'.ls',
|
|
|
|
'.ts',
|
|
|
|
],
|
|
|
|
},
|
|
|
|
this.config || {},
|
|
|
|
config
|
|
|
|
);
|
2016-03-02 17:07:05 +01:00
|
|
|
};
|
|
|
|
|
2019-06-04 00:37:17 +02:00
|
|
|
module.exports = Seeder;
|