You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
3.1 KiB
122 lines
3.1 KiB
2 years ago
|
const { URL, domainToUnicode } = require('url')
|
||
|
|
||
|
const CHAR_LOWERCASE_A = 97
|
||
|
const CHAR_LOWERCASE_Z = 122
|
||
|
|
||
|
const isWindows = process.platform === 'win32'
|
||
|
|
||
|
class ERR_INVALID_FILE_URL_HOST extends TypeError {
|
||
|
constructor (platform) {
|
||
|
super(`File URL host must be "localhost" or empty on ${platform}`)
|
||
|
this.code = 'ERR_INVALID_FILE_URL_HOST'
|
||
|
}
|
||
|
|
||
|
toString () {
|
||
|
return `${this.name} [${this.code}]: ${this.message}`
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class ERR_INVALID_FILE_URL_PATH extends TypeError {
|
||
|
constructor (msg) {
|
||
|
super(`File URL path ${msg}`)
|
||
|
this.code = 'ERR_INVALID_FILE_URL_PATH'
|
||
|
}
|
||
|
|
||
|
toString () {
|
||
|
return `${this.name} [${this.code}]: ${this.message}`
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class ERR_INVALID_ARG_TYPE extends TypeError {
|
||
|
constructor (name, actual) {
|
||
|
super(`The "${name}" argument must be one of type string or an instance ` +
|
||
|
`of URL. Received type ${typeof actual} ${actual}`)
|
||
|
this.code = 'ERR_INVALID_ARG_TYPE'
|
||
|
}
|
||
|
|
||
|
toString () {
|
||
|
return `${this.name} [${this.code}]: ${this.message}`
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class ERR_INVALID_URL_SCHEME extends TypeError {
|
||
|
constructor (expected) {
|
||
|
super(`The URL must be of scheme ${expected}`)
|
||
|
this.code = 'ERR_INVALID_URL_SCHEME'
|
||
|
}
|
||
|
|
||
|
toString () {
|
||
|
return `${this.name} [${this.code}]: ${this.message}`
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const isURLInstance = (input) => {
|
||
|
return input != null && input.href && input.origin
|
||
|
}
|
||
|
|
||
|
const getPathFromURLWin32 = (url) => {
|
||
|
const hostname = url.hostname
|
||
|
let pathname = url.pathname
|
||
|
for (let n = 0; n < pathname.length; n++) {
|
||
|
if (pathname[n] === '%') {
|
||
|
const third = pathname.codePointAt(n + 2) | 0x20
|
||
|
if ((pathname[n + 1] === '2' && third === 102) ||
|
||
|
(pathname[n + 1] === '5' && third === 99)) {
|
||
|
throw new ERR_INVALID_FILE_URL_PATH('must not include encoded \\ or / characters')
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pathname = pathname.replace(/\//g, '\\')
|
||
|
pathname = decodeURIComponent(pathname)
|
||
|
if (hostname !== '') {
|
||
|
return `\\\\${domainToUnicode(hostname)}${pathname}`
|
||
|
}
|
||
|
|
||
|
const letter = pathname.codePointAt(1) | 0x20
|
||
|
const sep = pathname[2]
|
||
|
if (letter < CHAR_LOWERCASE_A || letter > CHAR_LOWERCASE_Z ||
|
||
|
(sep !== ':')) {
|
||
|
throw new ERR_INVALID_FILE_URL_PATH('must be absolute')
|
||
|
}
|
||
|
|
||
|
return pathname.slice(1)
|
||
|
}
|
||
|
|
||
|
const getPathFromURLPosix = (url) => {
|
||
|
if (url.hostname !== '') {
|
||
|
throw new ERR_INVALID_FILE_URL_HOST(process.platform)
|
||
|
}
|
||
|
|
||
|
const pathname = url.pathname
|
||
|
|
||
|
for (let n = 0; n < pathname.length; n++) {
|
||
|
if (pathname[n] === '%') {
|
||
|
const third = pathname.codePointAt(n + 2) | 0x20
|
||
|
if (pathname[n + 1] === '2' && third === 102) {
|
||
|
throw new ERR_INVALID_FILE_URL_PATH('must not include encoded / characters')
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return decodeURIComponent(pathname)
|
||
|
}
|
||
|
|
||
|
const fileURLToPath = (path) => {
|
||
|
if (typeof path === 'string') {
|
||
|
path = new URL(path)
|
||
|
} else if (!isURLInstance(path)) {
|
||
|
throw new ERR_INVALID_ARG_TYPE('path', ['string', 'URL'], path)
|
||
|
}
|
||
|
|
||
|
if (path.protocol !== 'file:') {
|
||
|
throw new ERR_INVALID_URL_SCHEME('file')
|
||
|
}
|
||
|
|
||
|
return isWindows
|
||
|
? getPathFromURLWin32(path)
|
||
|
: getPathFromURLPosix(path)
|
||
|
}
|
||
|
|
||
|
module.exports = fileURLToPath
|