-
Notifications
You must be signed in to change notification settings - Fork 9
/
index.js
124 lines (108 loc) · 3.46 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
const { callbackify } = require('util');
const isSANB = require('is-string-and-not-blank');
const _ = require('lodash');
const slug = require('speakingurl');
// eslint-disable-next-line max-params
const getUniqueSlug = async (config, constructor, _id, str, i = 0) => {
if (!isSANB(str)) throw new Error('The `str` argument was missing');
const search = i === 0 ? str : config.slug(`${str}-${i}`, config.slugOptions);
const query = { _id: { $ne: _id } };
query[config.slugField] = search;
if (config.paranoid === 'hidden') query.hidden = { $ne: null };
const count = await constructor.countDocuments(query);
if (count === 0) return search;
return getUniqueSlug(config, constructor, _id, str, i + 1);
};
const mongooseSlugPlugin = (schema, options = {}) => {
const config = {
tmpl: '',
locals: {},
alwaysUpdateSlug: true,
slug,
errorMessage: 'Slug was missing or blank',
logger: console,
slugField: 'slug',
historyField: 'slug_history',
i18n: false,
slugOptions: {},
paranoid: false,
...options
};
const obj = {};
obj[config.slugField] = {
type: String,
index: true,
unique: true,
required: true,
trim: true,
set: val => config.slug(val, config.slugOptions),
validate: {
validator(val) {
const message =
config.i18n && config.i18n.t && this.locale
? config.i18n.t(config.errorMessage, this.locale)
: config.errorMessage;
if (!isSANB(val)) return Promise.reject(message);
Promise.resolve(true);
}
}
};
if (config.historyField) {
obj[config.historyField] = [
{
type: String,
index: true
}
];
}
schema.add(obj);
schema.pre('validate', async function(next) {
try {
const locals = { ...config.locals, ...this.toObject() };
const str = _.template(config.tmpl)(locals);
// set the slug if it is not already set
if (!isSANB(this[config.slugField]) || config.alwaysUpdateSlug) {
this[config.slugField] = config.slug(str, config.slugOptions);
} else {
// slugify the slug in case we set it manually and not in slug format
this[config.slugField] = config.slug(
this[config.slugField],
config.slugOptions
);
}
// ensure that the slug is unique
const uniqueSlug = await getUniqueSlug(
config,
this.constructor,
this._id,
this[config.slugField]
);
this[config.slugField] = uniqueSlug;
if (config.historyField) {
// create slug history if it does not exist yet
if (!Array.isArray(this[config.historyField]))
this[config.historyField] = [];
// add the slug to the slug_history
this[config.historyField].push(this[config.slugField]);
// make the slug history unique
this[config.historyField] = _.uniq(this[config.historyField]);
}
next();
} catch (err) {
config.logger.error(err);
if (config.i18n && config.i18n.t && this.locale)
err.message = config.i18n.t(config.errorMessage, this.locale);
else err.message = config.errorMessage;
next(err);
}
});
schema.statics.getUniqueSlug = function(_id, str) {
str = config.slug(str, config.slugOptions);
return getUniqueSlug(config, this, _id, str);
};
schema.statics.getUniqueSlugCallback = callbackify(
schema.statics.getUniqueSlug
);
return schema;
};
module.exports = mongooseSlugPlugin;