Skip to content

Commit 06ea5a5

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

File tree

3 files changed

+147
-1
lines changed

3 files changed

+147
-1
lines changed

extname.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
if (typeof process !== 'undefined' && process && process.platform === 'win32') {
14+
// normalize path
15+
path = path.replace(/\\/g, '/')
16+
}
17+
18+
let lastDotIndex = -1
19+
let filenameIndex = 0
20+
21+
// find last /
22+
for (let i = path.length - 1; i >= 0; i--) {
23+
if (path[i] === '/') {
24+
filenameIndex = i + 1
25+
break
26+
}
27+
}
28+
29+
// find last .
30+
for (let i = path.length - 1; i >= filenameIndex; i--) {
31+
if (path[i] === '.') {
32+
lastDotIndex = i
33+
break
34+
}
35+
}
36+
37+
if (lastDotIndex > filenameIndex) {
38+
// found ext
39+
return path.slice(lastDotIndex)
40+
}
41+
42+
return '' // no ext found
43+
}

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: 103 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,105 @@ 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+
if (process.platform === 'win32') {
286+
it('should return the correct extension for a Windows path with backslashes', function () {
287+
const result = extname('C:\\Users\\file.txt')
288+
assert.strictEqual(result, '.txt')
289+
})
290+
291+
it('should return the correct extension for a Windows path with multiple backslashes', function () {
292+
const result = extname('C:\\Users\\Documents\\Projects\\file.tar.gz')
293+
assert.strictEqual(result, '.gz')
294+
})
295+
296+
it('should return an empty string for a Windows path with no extension', function () {
297+
const result = extname('C:\\Users\\file')
298+
assert.strictEqual(result, '')
299+
})
300+
301+
it('should return an empty string for a hidden Windows file (starts with a dot)', function () {
302+
const result = extname('C:\\Users\\.hiddenfile')
303+
assert.strictEqual(result, '')
304+
})
305+
306+
it('should return the correct extension for a Windows path with multiple dots', function () {
307+
const result = extname('C:\\Users\\file.name.with.dots.ext')
308+
assert.strictEqual(result, '.ext')
309+
})
310+
311+
it('should return the correct extension for a Windows path with mixed case extension', function () {
312+
const result = extname('C:\\Users\\file.TXT')
313+
assert.strictEqual(result, '.TXT')
314+
})
315+
}
316+
317+
// Test for TypeError when input is not a string
318+
it('should throw a TypeError if the input is not a string', function () {
319+
assert.throws(() => extname(123), {
320+
name: 'TypeError',
321+
message: 'path is not a string'
322+
})
323+
assert.throws(() => extname(null), {
324+
name: 'TypeError',
325+
message: 'path is not a string'
326+
})
327+
assert.throws(() => extname(undefined), {
328+
name: 'TypeError',
329+
message: 'path is not a string'
330+
})
331+
assert.throws(() => extname({}), {
332+
name: 'TypeError',
333+
message: 'path is not a string'
334+
})
335+
assert.throws(() => extname([]), {
336+
name: 'TypeError',
337+
message: 'path is not a string'
338+
})
339+
})
340+
})

0 commit comments

Comments
 (0)