diff --git a/extname.js b/extname.js new file mode 100644 index 0000000..c077807 --- /dev/null +++ b/extname.js @@ -0,0 +1,42 @@ +'use strict' + +/** + * Return the extension of the path, from the last '.' to end of string in the last portion of the path. If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string. + * @param {String} path — the path to evaluate + * @throws {TypeError} if path is not a string + */ +module.exports = (path) => { + if (typeof path !== 'string') { + throw new TypeError('path is not a string') + } + + // normalize path (win -> posix) + // not too happy with doing this on every call, but avoids test coverage dropping + path = path.replace(/\\/g, '/') + + let lastDotIndex = -1 + let filenameIndex = 0 + + // find last / + for (let i = path.length - 1; i >= 0; i--) { + if (path[i] === '/') { + filenameIndex = i + 1 + break + } + } + + // find last . + for (let i = path.length - 1; i >= filenameIndex; i--) { + if (path[i] === '.') { + lastDotIndex = i + break + } + } + + if (lastDotIndex > filenameIndex) { + // found ext + return path.slice(lastDotIndex) + } + + return '' // no ext found +} diff --git a/index.js b/index.js index c348cb6..10fd2ff 100644 --- a/index.js +++ b/index.js @@ -13,7 +13,7 @@ */ var db = require('mime-db') -var extname = require('path').extname +var extname = require('./extname') var mimeScore = require('./mimeScore') /** diff --git a/test/test.js b/test/test.js index d098d28..13590d1 100644 --- a/test/test.js +++ b/test/test.js @@ -1,5 +1,6 @@ var assert = require('assert') var mimeTypes = require('..') +const extname = require('../extname') describe('mimeTypes', function () { describe('.charset(type)', function () { @@ -235,3 +236,103 @@ describe('mimeTypes', function () { } }) }) + +describe('extname', function () { + it('should return the correct extension for a file with a valid extension', function () { + const result = extname('file.txt') + assert.strictEqual(result, '.txt') + }) + + it('should return an empty string if no extension exists', function () { + const result = extname('file') + assert.strictEqual(result, '') + }) + + it('should return an empty string for files that start with a dot but have no extension', function () { + const result = extname('.hiddenfile') + assert.strictEqual(result, '') + }) + + it('should return the correct extension for files with multiple dots in the name', function () { + const result = extname('archive.tar.gz') + assert.strictEqual(result, '.gz') + }) + + it('should return the correct extension for a file with mixed case extension', function () { + const result = extname('file.TXT') + assert.strictEqual(result, '.TXT') + }) + + it('should return an empty string if the dot is at the start of the filename', function () { + const result = extname('.file') + assert.strictEqual(result, '') + }) + + // POSIX: + + it('should return the correct extension for a file in a nested directory', function () { + const result = extname('folder/subfolder/file.txt') + assert.strictEqual(result, '.txt') + }) + + it('should handle paths with multiple slashes correctly', function () { + const result = extname('/home/user/file.name.ext') + assert.strictEqual(result, '.ext') + }) + + // Windows: + + it('should return the correct extension for a Windows path with backslashes', function () { + const result = extname('C:\\Users\\file.txt') + assert.strictEqual(result, '.txt') + }) + + it('should return the correct extension for a Windows path with multiple backslashes', function () { + const result = extname('C:\\Users\\Documents\\Projects\\file.tar.gz') + assert.strictEqual(result, '.gz') + }) + + it('should return an empty string for a Windows path with no extension', function () { + const result = extname('C:\\Users\\file') + assert.strictEqual(result, '') + }) + + it('should return an empty string for a hidden Windows file (starts with a dot)', function () { + const result = extname('C:\\Users\\.hiddenfile') + assert.strictEqual(result, '') + }) + + it('should return the correct extension for a Windows path with multiple dots', function () { + const result = extname('C:\\Users\\file.name.with.dots.ext') + assert.strictEqual(result, '.ext') + }) + + it('should return the correct extension for a Windows path with mixed case extension', function () { + const result = extname('C:\\Users\\file.TXT') + assert.strictEqual(result, '.TXT') + }) + + // Test for TypeError when input is not a string + it('should throw a TypeError if the input is not a string', function () { + assert.throws(() => extname(123), { + name: 'TypeError', + message: 'path is not a string' + }) + assert.throws(() => extname(null), { + name: 'TypeError', + message: 'path is not a string' + }) + assert.throws(() => extname(undefined), { + name: 'TypeError', + message: 'path is not a string' + }) + assert.throws(() => extname({}), { + name: 'TypeError', + message: 'path is not a string' + }) + assert.throws(() => extname([]), { + name: 'TypeError', + message: 'path is not a string' + }) + }) +})