diff --git a/lib/index.js b/lib/index.js index d2b124e..ffa9a0f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,12 +7,23 @@ * @typedef {'basename' | 'dirname' | 'extname' | 'path' | 'stem'} Field * Fields related to paths. * - * @typedef SpecAffix - * Define prepending and/or appending. - * @property {string | null | undefined} [prefix] - * Substring to prepend in front of the field. - * @property {string | null | undefined} [suffix] - * Substring to append after the field. + * @callback Move + * Move. + * @param {VFile} file + * File to change. + * @returns {undefined} + * Nothing. + * + * @typedef {Array | Move | Spec | string} Renames + * Rename instructions. + * + * * if the bound rename is a normal string starting with a dot (`.`), sets + * `file.extname` + * * otherwise, if the bound rename is a normal string, sets `file.basename` + * * otherwise, if the bound test is an array, all renames in it are + * performed + * * otherwise, if the bound rename is an object, renames according to the + * `Spec` * * @typedef Spec * An object describing path properties to values. @@ -31,32 +42,15 @@ * Change file path (`'~/index.min.js'`). * @property {VFileOptions['stem'] | SpecAffix} [stem] * Change stem (`'index.min'`). - */ - -/** - * @callback Move - * Move. - * @param {VFile} file - * File to change. - * @returns {undefined} - * Nothing. - */ - -/** - * @typedef {Array | Move | Spec | string} Renames - * Rename instructions. * - * * if the bound rename is a normal string starting with a dot (`.`), sets - * `file.extname` - * * otherwise, if the bound rename is a normal string, sets `file.basename` - * * otherwise, if the bound test is an array, all renames in it are - * performed - * * otherwise, if the bound rename is an object, renames according to the - * `Spec` + * @typedef SpecAffix + * Define prepending and/or appending. + * @property {string | null | undefined} [prefix] + * Substring to prepend in front of the field. + * @property {string | null | undefined} [suffix] + * Substring to append after the field. */ -const own = {}.hasOwnProperty - // Order of renaming properties. // See // Other properties are invalid. @@ -68,22 +62,7 @@ const order = /** @type {const} */ ([ 'dirname' ]) -/** - * Rename a file. - * - * When given something, returns a vfile from that, and changes its path - * properties. - * - * @param {VFile} file - * File to rename. - * @param {Renames | null | undefined} [renames] - * Rename instructions. - * @returns {undefined} - * Nothing. - */ -export function rename(file, renames) { - convert(renames)(file) -} +const own = {}.hasOwnProperty /** * Create a function (the move) from `renames`, that when given a file changes @@ -104,77 +83,72 @@ export function convert(renames) { } if (typeof renames === 'string') { - return setter(renames.charAt(0) === '.' ? 'extname' : 'basename', renames) + return createSetterMove( + renames.charAt(0) === '.' ? 'extname' : 'basename', + renames + ) } if (typeof renames === 'object') { return Array.isArray(renames) - ? allFactory(convertAll(renames)) - : specFactory(renames) + ? createMoves(renames) + : createSpecMove(renames) } throw new Error('Expected function, string, array, or object as renames') } /** - * @param {Spec} spec + * Rename a file. + * + * When given something, returns a vfile from that, and changes its path + * properties. + * + * @param {VFile} file + * File to rename. + * @param {Renames | null | undefined} [renames] + * Rename instructions. + * @returns {undefined} + * Nothing. + */ +export function rename(file, renames) { + convert(renames)(file) +} + +/** + * Create a move from multiples moves. + * + * @param {Array} moves + * Moves. * @returns {Move} + * Move. */ -function specFactory(spec) { - /** @type {Array} */ - const props = [] - /** @type {Array} */ - const moves = [] - /** @type {Field} */ - let prop +function combineMoves(moves) { + return move - // Fail on non-path props. - for (prop in spec) { - if (own.call(spec, prop)) { - if (!order.includes(prop)) { - throw new Error( - 'Cannot rename `' + prop + '`: it’s not a path property' - ) - } + /** @type {Move} */ + function move(file) { + const history = [...file.history] + let index = -1 - props.push(prop) + while (++index < moves.length) { + moves[index](file) } - } - - // Create moves for all specs. - props.sort(sort) - let index = -1 - - while (++index < props.length) { - const prop = props[index] - const value = spec[prop] - - if (typeof value === 'string') { - moves.push(setter(prop, value)) - } else if (value) { - if ('prefix' in value && value.prefix) { - moves.push(prefix(prop, value.prefix)) - } - - if ('suffix' in value && value.suffix) { - moves.push(suffix(prop, value.suffix)) - } - } + // Clean history to only include one changed path. + file.history = [...history, file.path] } - - return allFactory(moves) } /** - * Convert renames into moves. + * Convert renames into a move. * - * @param {Array} renames + * @param {Array} renames * Renames. - * @returns {Array} - * Moves. + * @returns {Move} + * Move. */ -function convertAll(renames) { +function createMoves(renames) { /** @type {Array} */ const moves = [] let index = -1 @@ -183,35 +157,29 @@ function convertAll(renames) { moves[index] = convert(renames[index]) } - return moves + return combineMoves(moves) } /** - * Create a move from multiples moves. + * Create a move that prepends. * - * @param {Array} changes - * Moves. + * @param {Field} key + * Field. + * @param {string} prefix + * Value to add. * @returns {Move} * Move. */ -function allFactory(changes) { - return all +function createPrefixMove(key, prefix) { + return add /** @type {Move} */ - function all(file) { - const history = file.history.concat() - let index = -1 - - while (++index < changes.length) { - changes[index](file) - } - - // Clean history to only include one changed path. - file.history = [...history, file.path] + function add(file) { + file[key] = prefix + file[key] } } /** - * Create a move that sets. + * Create a move that sets a field. * * @param {Field} key * Field. @@ -220,30 +188,67 @@ function allFactory(changes) { * @returns {Move} * Move. */ -function setter(key, value) { - return set +function createSetterMove(key, value) { + return move + /** @type {Move} */ - function set(file) { + function move(file) { file[key] = value } } /** - * Create a move that prepends. + * Convert a spec into a move. * - * @param {Field} key - * Field. - * @param {string} prefix - * Value to add. + * @param {Spec} spec + * Spec. * @returns {Move} * Move. */ -function prefix(key, prefix) { - return add - /** @type {Move} */ - function add(file) { - file[key] = prefix + file[key] +function createSpecMove(spec) { + /** @type {Array} */ + const props = [] + /** @type {Array} */ + const moves = [] + /** @type {Field} */ + let prop + + // Fail on non-path props. + for (prop in spec) { + if (own.call(spec, prop)) { + if (!order.includes(prop)) { + throw new Error( + 'Cannot rename `' + prop + '`: it’s not a path property' + ) + } + + props.push(prop) + } + } + + // Create moves for all specs. + props.sort(sort) + + let index = -1 + + while (++index < props.length) { + const prop = props[index] + const value = spec[prop] + + if (typeof value === 'string') { + moves.push(createSetterMove(prop, value)) + } else if (value) { + if ('prefix' in value && value.prefix) { + moves.push(createPrefixMove(prop, value.prefix)) + } + + if ('suffix' in value && value.suffix) { + moves.push(createSuffixMove(prop, value.suffix)) + } + } } + + return combineMoves(moves) } /** @@ -256,7 +261,7 @@ function prefix(key, prefix) { * @returns {Move} * Move. */ -function suffix(key, suffix) { +function createSuffixMove(key, suffix) { return add /** @type {Move} */ function add(file) { diff --git a/readme.md b/readme.md index 6e563e9..63c181c 100644 --- a/readme.md +++ b/readme.md @@ -157,7 +157,7 @@ Rename instructions (TypeScript type). ###### Type ```ts -type Renames = string | Move | Spec | Array +type Renames = Array | Move | Spec | string ``` ### `Spec`