Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions extname.js
Original file line number Diff line number Diff line change
@@ -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--) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all this can be done more elegantly, but at a cost to perf

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
}
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/

var db = require('mime-db')
var extname = require('path').extname
var extname = require('./extname')
var mimeScore = require('./mimeScore')

/**
Expand Down
101 changes: 101 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var assert = require('assert')
var mimeTypes = require('..')
const extname = require('../extname')

describe('mimeTypes', function () {
describe('.charset(type)', function () {
Expand Down Expand Up @@ -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'
})
})
})
Loading