Skip to content

Commit

Permalink
feat(utils): changeExt
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <[email protected]>
  • Loading branch information
unicornware committed Dec 11, 2022
1 parent 98c393a commit 10b1917
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/utils/__tests__/change-ext.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* @file Unit Tests - changeExt
* @module pathe/utils/tests/unit/changeExt
*/

import testSubject from '../change-ext'

describe('unit:utils/changeExt', () => {
it('should return path with new file extension', () => {
// Arrange
const cases: [...Parameters<typeof testSubject>, string][] = [
['file', 'mjs', 'file.mjs'],
['file', 'mts', 'file.mts'],
['file.', '.mjs', 'file.mjs'],
['file.', '.mts', 'file.mts'],
['file.mts', 'd.mts', 'file.d.mts'],
['file.min.cjs', '.mjs', 'file.min.mjs']
]

// Act + Expect
cases.forEach(([path, ext, expected]) => {
expect(testSubject(path, ext)).to.equal(expected)
})
})

it('should return path without modications if ext is empty', () => {
// Arrange
const cases: [...Parameters<typeof testSubject>, string][] = [
['file.cjs', '', 'file.cjs'],
['file.cts', ' ', 'file.cts'],
['file.mjs', null, 'file.mjs'],
['file.mts', undefined, 'file.mts']
]

// Act + Expect
cases.forEach(([path, ext, expected]) => {
expect(testSubject(path, ext)).to.equal(expected)
})
})
})
56 changes: 56 additions & 0 deletions src/utils/change-ext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @file Utilities - changeExt
* @module pathe/utils/changeExt
*/

import validateString from '#src/internal/validate-string'
import extname from '#src/lib/extname'
import type { Ext } from '#src/types'
import type { EmptyString, Nullable } from '@flex-development/tutils'
import addExt from './add-ext'
import formatExt from './format-ext'

/**
* Changes the file extension of the given `path`.
*
* Does nothing if a file extension isn't provided.
*
* @example
* changeExt('file') // 'file'
* @example
* changeExt('file', 'cjs') // 'file.cjs'
* @example
* changeExt('file', '.mjs') // 'file.mjs'
* @example
* changeExt('file.mts', '.d.mts') // 'file.d.mts'
* @example
* changeExt('file.min.cjs', 'mjs') // 'file.min.mjs'
*
* @param {string} path - Path to evaluate
* @param {Nullable<string>} [ext] - File extension to add
* @return {string} `path` unmodified or with new file extension
* @throws {TypeError} If `path` is not a string or `ext` is not a string
*/
const changeExt = (path: string, ext?: Nullable<string>): string => {
validateString(path, 'path')

// exit early if extension isn't provided or validate ext
if (ext === null || ext === undefined) return path
else validateString(ext, 'ext')

// exit early if extension is empty string
if (!ext.trim()) return path

/**
* File extension of {@linkcode path}.
*
* @const {EmptyString | Ext} extension
*/
const extension: EmptyString | Ext = extname((path = path.replace(/\.$/, '')))

return extension
? path.replace(new RegExp(`\\${extension}$`), formatExt(ext))
: addExt(path, ext)
}

export default changeExt
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
*/

export { default as addExt } from './add-ext'
export { default as changeExt } from './change-ext'
export { default as formatExt } from './format-ext'
export { default as removeExt } from './remove-ext'

0 comments on commit 10b1917

Please sign in to comment.