164 lines
4.3 KiB
JavaScript
164 lines
4.3 KiB
JavaScript
|
// Seeder
|
||
|
// -------
|
||
|
|
||
|
const fs = require('fs');
|
||
|
const path = require('path');
|
||
|
const { promisify } = require('util');
|
||
|
const mkdirp = require('mkdirp');
|
||
|
const { filter, includes, extend } = require('lodash');
|
||
|
const { writeJsFileUsingTemplate } = require('../util/template');
|
||
|
|
||
|
// The new seeds we're performing, typically called from the `knex.seed`
|
||
|
// interface on the main `knex` object. Passes the `knex` instance performing
|
||
|
// the seeds.
|
||
|
class Seeder {
|
||
|
constructor(knex) {
|
||
|
this.knex = knex;
|
||
|
this.config = this.setConfig(knex.client.config.seeds);
|
||
|
}
|
||
|
|
||
|
// Runs seed files for the given environment.
|
||
|
async run(config) {
|
||
|
this.config = this.setConfig(config);
|
||
|
const all = await this._listAll();
|
||
|
const files =
|
||
|
config && config.specific
|
||
|
? all.filter((file) => file === config.specific)
|
||
|
: all;
|
||
|
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);
|
||
|
const loadExtensions = this.config.loadExtensions;
|
||
|
return promisify(fs.readdir)(this._absoluteConfigDir()).then((seeds) =>
|
||
|
filter(seeds, (value) => {
|
||
|
const extension = path.extname(value);
|
||
|
return includes(loadExtensions, extension);
|
||
|
}).sort()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Ensures a folder for the seeds exist, dependent on the
|
||
|
// seed config settings.
|
||
|
async _ensureFolder() {
|
||
|
const dir = this._absoluteConfigDir();
|
||
|
try {
|
||
|
await promisify(fs.stat)(dir);
|
||
|
} catch (e) {
|
||
|
await promisify(mkdirp)(dir);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Run seed files, in sequence.
|
||
|
_runSeeds(seeds) {
|
||
|
seeds.forEach((seed) => this._validateSeedStructure(seed));
|
||
|
return this._waterfallBatch(seeds);
|
||
|
}
|
||
|
|
||
|
// Validates seed files by requiring and checking for a `seed` function.
|
||
|
_validateSeedStructure(name) {
|
||
|
const seed = require(path.join(this._absoluteConfigDir(), name));
|
||
|
if (typeof seed.seed !== 'function') {
|
||
|
throw new Error(`Invalid seed file: ${name} must have a seed function`);
|
||
|
}
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
_getStubPath() {
|
||
|
return (
|
||
|
this.config.stub ||
|
||
|
path.join(__dirname, 'stub', this.config.extension + '.stub')
|
||
|
);
|
||
|
}
|
||
|
|
||
|
_getNewStubFileName(name) {
|
||
|
if (name[0] === '-') name = name.slice(1);
|
||
|
return name + '.' + this.config.extension;
|
||
|
}
|
||
|
|
||
|
_getNewStubFilePath(name) {
|
||
|
return path.join(this._absoluteConfigDir(), this._getNewStubFileName(name));
|
||
|
}
|
||
|
|
||
|
// 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.
|
||
|
async _waterfallBatch(seeds) {
|
||
|
const { knex } = this;
|
||
|
const seedDirectory = this._absoluteConfigDir();
|
||
|
const log = [];
|
||
|
for (const seedName of seeds) {
|
||
|
const seedPath = path.join(seedDirectory, seedName);
|
||
|
const seed = require(seedPath);
|
||
|
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 =
|
||
|
error.stack
|
||
|
.split('\n')
|
||
|
.slice(0, 2)
|
||
|
.join('\n') +
|
||
|
'\n' +
|
||
|
originalError.stack;
|
||
|
throw error;
|
||
|
}
|
||
|
}
|
||
|
return [log];
|
||
|
}
|
||
|
|
||
|
_absoluteConfigDir() {
|
||
|
return path.resolve(process.cwd(), this.config.directory);
|
||
|
}
|
||
|
|
||
|
setConfig(config) {
|
||
|
return extend(
|
||
|
{
|
||
|
extension: 'js',
|
||
|
directory: './seeds',
|
||
|
loadExtensions: [
|
||
|
'.co',
|
||
|
'.coffee',
|
||
|
'.eg',
|
||
|
'.iced',
|
||
|
'.js',
|
||
|
'.litcoffee',
|
||
|
'.ls',
|
||
|
'.ts',
|
||
|
],
|
||
|
},
|
||
|
this.config || {},
|
||
|
config
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = Seeder;
|