forked from barsh/true-case-path
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
94 lines (85 loc) · 2.44 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
'use strict'
const { readdir: _readdir, readdirSync } = require('fs')
const { platform } = require('os')
const { isAbsolute, normalize } = require('path')
const { promisify: pify } = require('util')
const readdir = pify(_readdir)
const isWindows = platform() === 'win32'
const delimiter = isWindows ? '\\' : '/'
module.exports = {
trueCasePath: _trueCasePath({ sync: false }),
trueCasePathSync: _trueCasePath({ sync: true })
}
function getRelevantFilePathSegments(filePath) {
return filePath.split(delimiter).filter((s) => s !== '')
}
function escapeString(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
function matchCaseInsensitive(fileOrDirectory, directoryContents, filePath) {
const caseInsensitiveRegex = new RegExp(
`^${escapeString(fileOrDirectory)}$`,
'i'
)
for (const file of directoryContents) {
if (caseInsensitiveRegex.test(file)) return file
}
throw new Error(
`[true-case-path]: Called with ${filePath}, but no matching file exists`
)
}
function _trueCasePath({ sync }) {
return (filePath, basePath) => {
if (basePath) {
if (!isAbsolute(basePath)) {
throw new Error(
`[true-case-path]: basePath argument must be absolute. Received "${basePath}"`
)
}
basePath = normalize(basePath)
}
filePath = normalize(filePath)
const segments = getRelevantFilePathSegments(filePath)
if (isAbsolute(filePath)) {
if (basePath) {
throw new Error(
'[true-case-path]: filePath must be relative when used with basePath'
)
}
basePath = isWindows
? segments.shift().toUpperCase() // drive letter
: ''
} else if (!basePath) {
basePath = process.cwd()
}
return sync
? iterateSync(basePath, filePath, segments)
: iterateAsync(basePath, filePath, segments)
}
}
function iterateSync(basePath, filePath, segments) {
return segments.reduce(
(realPath, fileOrDirectory) =>
realPath +
delimiter +
matchCaseInsensitive(
fileOrDirectory,
readdirSync(realPath + delimiter),
filePath
),
basePath
)
}
async function iterateAsync(basePath, filePath, segments) {
return await segments.reduce(
async (realPathPromise, fileOrDirectory) =>
(await realPathPromise) +
delimiter +
matchCaseInsensitive(
fileOrDirectory,
await readdir((await realPathPromise) + delimiter),
filePath
),
basePath
)
}