Skip to content

Commit 83098de

Browse files
committed
➖ remove dependency on node:path; reinvent wheel
1 parent 64e03d9 commit 83098de

File tree

3 files changed

+144
-1
lines changed

3 files changed

+144
-1
lines changed

extname.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict'
2+
3+
/**
4+
* 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.
5+
* @param {String} path — the path to evaluate
6+
* @throws {TypeError} if path is not a string
7+
*/
8+
module.exports = (path) => {
9+
if (typeof path !== 'string') {
10+
throw new TypeError('path is not a string')
11+
}
12+
13+
// normalize path (win -> posix)
14+
// not too happy with doing this on every call, but avoids test coverage dropping
15+
path = path.replace(/\\/g, '/')
16+
17+
let lastDotIndex = -1
18+
let filenameIndex = 0
19+
20+
// find last /
21+
for (let i = path.length - 1; i >= 0; i--) {
22+
if (path[i] === '/') {
23+
filenameIndex = i + 1
24+
break
25+
}
26+
}
27+
28+
// find last .
29+
for (let i = path.length - 1; i >= filenameIndex; i--) {
30+
if (path[i] === '.') {
31+
lastDotIndex = i
32+
break
33+
}
34+
}
35+
36+
if (lastDotIndex > filenameIndex) {
37+
// found ext
38+
return path.slice(lastDotIndex)
39+
}
40+
41+
return '' // no ext found
42+
}

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*/
1414

1515
var db = require('mime-db')
16-
var extname = require('path').extname
16+
var extname = require('./extname')
1717
var mimeScore = require('./mimeScore')
1818

1919
/**

test/test.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var assert = require('assert')
22
var mimeTypes = require('..')
3+
const extname = require('../extname')
34

45
describe('mimeTypes', function () {
56
describe('.charset(type)', function () {
@@ -235,3 +236,103 @@ describe('mimeTypes', function () {
235236
}
236237
})
237238
})
239+
240+
describe('extname', function () {
241+
it('should return the correct extension for a file with a valid extension', function () {
242+
const result = extname('file.txt')
243+
assert.strictEqual(result, '.txt')
244+
})
245+
246+
it('should return an empty string if no extension exists', function () {
247+
const result = extname('file')
248+
assert.strictEqual(result, '')
249+
})
250+
251+
it('should return an empty string for files that start with a dot but have no extension', function () {
252+
const result = extname('.hiddenfile')
253+
assert.strictEqual(result, '')
254+
})
255+
256+
it('should return the correct extension for files with multiple dots in the name', function () {
257+
const result = extname('archive.tar.gz')
258+
assert.strictEqual(result, '.gz')
259+
})
260+
261+
it('should return the correct extension for a file with mixed case extension', function () {
262+
const result = extname('file.TXT')
263+
assert.strictEqual(result, '.TXT')
264+
})
265+
266+
it('should return an empty string if the dot is at the start of the filename', function () {
267+
const result = extname('.file')
268+
assert.strictEqual(result, '')
269+
})
270+
271+
// POSIX:
272+
273+
it('should return the correct extension for a file in a nested directory', function () {
274+
const result = extname('folder/subfolder/file.txt')
275+
assert.strictEqual(result, '.txt')
276+
})
277+
278+
it('should handle paths with multiple slashes correctly', function () {
279+
const result = extname('/home/user/file.name.ext')
280+
assert.strictEqual(result, '.ext')
281+
})
282+
283+
// Windows:
284+
285+
it('should return the correct extension for a Windows path with backslashes', function () {
286+
const result = extname('C:\\Users\\file.txt')
287+
assert.strictEqual(result, '.txt')
288+
})
289+
290+
it('should return the correct extension for a Windows path with multiple backslashes', function () {
291+
const result = extname('C:\\Users\\Documents\\Projects\\file.tar.gz')
292+
assert.strictEqual(result, '.gz')
293+
})
294+
295+
it('should return an empty string for a Windows path with no extension', function () {
296+
const result = extname('C:\\Users\\file')
297+
assert.strictEqual(result, '')
298+
})
299+
300+
it('should return an empty string for a hidden Windows file (starts with a dot)', function () {
301+
const result = extname('C:\\Users\\.hiddenfile')
302+
assert.strictEqual(result, '')
303+
})
304+
305+
it('should return the correct extension for a Windows path with multiple dots', function () {
306+
const result = extname('C:\\Users\\file.name.with.dots.ext')
307+
assert.strictEqual(result, '.ext')
308+
})
309+
310+
it('should return the correct extension for a Windows path with mixed case extension', function () {
311+
const result = extname('C:\\Users\\file.TXT')
312+
assert.strictEqual(result, '.TXT')
313+
})
314+
315+
// Test for TypeError when input is not a string
316+
it('should throw a TypeError if the input is not a string', function () {
317+
assert.throws(() => extname(123), {
318+
name: 'TypeError',
319+
message: 'path is not a string'
320+
})
321+
assert.throws(() => extname(null), {
322+
name: 'TypeError',
323+
message: 'path is not a string'
324+
})
325+
assert.throws(() => extname(undefined), {
326+
name: 'TypeError',
327+
message: 'path is not a string'
328+
})
329+
assert.throws(() => extname({}), {
330+
name: 'TypeError',
331+
message: 'path is not a string'
332+
})
333+
assert.throws(() => extname([]), {
334+
name: 'TypeError',
335+
message: 'path is not a string'
336+
})
337+
})
338+
})

0 commit comments

Comments
 (0)