From 46847676316eb11bbdea59b7431054b71683b4e2 Mon Sep 17 00:00:00 2001 From: Kevin Van Lierde Date: Sun, 30 Jan 2022 03:30:39 +0100 Subject: [PATCH] feat: proper jsdocs, cleaner option handling code --- lib/index.js | 126 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 42 deletions(-) diff --git a/lib/index.js b/lib/index.js index fd8bc30..5a8fc9e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,6 +8,62 @@ const substitute = require('substitute'); const error = debug.extend('error'); +/** + * [Slugify options](https://github.com/simov/slugify#options) + * + * @typedef {Object} SlugifyOptions + * @property {boolean} extend extend known unicode symbols with a `{'char': 'replacement'}` object + * @property {string} [replacement='-'] replace spaces with replacement character, defaults to `-` + * @property {RegExp} [remove] remove characters that match regex + * @property {boolean} [lower=true] convert to lower case, defaults to `true` + * @property {boolean} [strict=false] strip special characters except replacement, defaults to `false` + * @property {string} [locale] language code of the locale to use + * @property {boolean} trim trim leading and trailing replacement chars, defaults to `true` + */ + +/** + * @callback slugFunction + * @param {string} filepath + * @returns {string} slug + */ + +/** + * Linkset definition + * + * @typedef {Object} Linkset + * @property {boolean} [isDefault] Whether this linkset should be used as the default instead + * @property {Object.} match An object whose key:value pairs will be used to match files and transform their permalinks according to the rules in this linkset + * @property {string} pattern A permalink pattern to transform file paths into, e.g. `blog/:date/:title` + * @property {SlugifyOptions|slugFunction} [slug] [Slugify options](https://github.com/simov/slugify) or a custom slug function of the form `(pathpart) => string` + * @property {string} [date='YYYY/MM/DD'] [Moment.js format string](https://momentjs.com/docs/#/displaying/format/) to transform Date link parts into, defaults to `YYYY/MM/DD`. + */ + +/** + * `@metalsmith/permalinks` options & default linkset + * + * @typedef {Object} Options + * @property {string} [pattern] A permalink pattern to transform file paths into, e.g. `blog/:date/:title` + * @property {string} [date='YYYY/MM/DD'] [Moment.js format string](https://momentjs.com/docs/#/displaying/format/) to transform Date link parts into, defaults to `YYYY/MM/DD`. + * @property {boolean|'folder'} [relative=true] When `true` (by default), will duplicate sibling files so relative links keep working in resulting structure. Turn off by setting `false`. Can also be set to `folder`, which uses a strategy that considers files in folder as siblings if the folder is named after the html file. + * @property {string} [indexFile='index.html'] Basename of the permalinked file (default: `index.html`) + * @property {boolean|Function} [unique] Set to `true` to add a number to duplicate permalinks (default: `false`), or specify a custom duplicate handling callback of the form `(permalink, files, file, options) => string` + * @property {boolean} [duplicatesFail=false] Set to `true` to throw an error if multiple file path transforms result in the same permalink. `false` by default + * @property {Linkset[]} [linksets] An array of additional linksets + * @property {SlugifyOptions|slugFunction} [slug] [Slugify options](https://github.com/simov/slugify) or a custom slug function of the form `(pathpart) => string` + */ + +/** @type {Options} */ +const defaultOptions = { + pattern: null, + date: 'YYYY/MM/DD', + slug: { lower: true }, + relative: true, + indexFile: 'index.html', + unique: false, + duplicatesFail: false, + linksets: [], +} + /** * Maps the slugify function to slug to maintain compatibility * @@ -16,19 +72,20 @@ const error = debug.extend('error'); * * @return {String} */ -const slug = (text, options = {}) => { - // extend if it's an object - if (typeof options.extend === 'object' && options.extend !== null) { - slugify.extend(options.extend); - } +function slugFn(options = defaultOptions) { + return text => { + if (typeof options.extend === 'object' && options.extend !== null) { + slugify.extend(options.extend); + } - return slugify(text, Object.assign({}, { lower: true }, options)); + return slugify(text, Object.assign({}, { lower: true }, options)); + } }; /** * Re-links content * - * @param {Object} data + * @param {import('metalsmith').File} data * @param {Object} moved * * @return {Void} @@ -45,31 +102,25 @@ const relink = (data, moved) => { /** * Normalize an options argument. * - * @param {String/Object} options - * + * @param {string|Options} options * @return {Object} */ -const normalize = options => { +const normalizeOptions = options => { if (typeof options === 'string') { options = { pattern: options }; } - - options = options || {}; - options.date = - typeof options.date === 'string' - ? format(options.date) - : format('YYYY/MM/DD'); - options.relative = Object.prototype.hasOwnProperty.call(options, 'relative') - ? options.relative - : true; - options.linksets = options.linksets || []; + options = Object.assign({}, defaultOptions, options) + options.date = format(options.date) + if (typeof options.slug !== 'function') { + options.slug = slugFn(options.slug) + } return options; }; /** * Return a formatter for a given moment.js format `string`. * - * @param {String} string + * @param {string} string * @return {Function} */ const format = string => date => moment(date).utc().format(string); @@ -77,7 +128,7 @@ const format = string => date => moment(date).utc().format(string); /** * Get a list of sibling and children files for a given `file` in `files`. * - * @param {String} file + * @param {string} file * @param {Object} files * @return {Object} */ @@ -104,7 +155,7 @@ const family = (file, files) => { /** * Get a list of files that exists in a folder named after `file` for a given `file` in `files`. * - * @param {String} file + * @param {string} file * @param {Object} files * @return {Object} */ @@ -151,7 +202,7 @@ const resolve = str => { /** * Replace a `pattern` with a file's `data`. * - * @param {String} pattern (optional) + * @param {string} pattern (optional) * @param {Object} data * @param {Object} options * @@ -168,10 +219,7 @@ const replace = (pattern, data, options) => { if (val instanceof Date) { ret[key] = options.date(val); } else { - ret[key] = - typeof options.slug === 'function' - ? options.slug(val.toString()) - : slug(val.toString(), options.slug); + ret[key] = options.slug(val.toString()); } } @@ -181,7 +229,7 @@ const replace = (pattern, data, options) => { /** * Get the params from a `pattern` string. * - * @param {String} pattern + * @param {string} pattern * @return {Array} */ const params = pattern => { @@ -195,8 +243,8 @@ const params = pattern => { /** * Check whether a file is an HTML file. * - * @param {String} str The path - * @return {Boolean} + * @param {string} str The path + * @return {boolean} */ const html = str => path.extname(str) === '.html'; @@ -204,17 +252,11 @@ const html = str => path.extname(str) === '.html'; * Metalsmith plugin that renames files so that they're permalinked properly * for a static site, aka that `about.html` becomes `about/index.html`. * - * @param {Object} options - * @property {String} pattern - * @property {String/Function} date - * @property {String} indexFile - * @property {Boolean/Function} unique - * @property {Boolean} duplicatesFail - * - * @return {Function} + * @param {Options} options + * @returns {import('metalsmith').Plugin} */ -const plugin = options => { - options = normalize(options); +function inititalizePermalinks(options) { + options = normalizeOptions(options); const { linksets } = options; let defaultLinkset = linksets.find(ls => { @@ -340,4 +382,4 @@ const plugin = options => { }; // Expose `plugin` -module.exports = plugin; +module.exports = inititalizePermalinks; \ No newline at end of file