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.
121 lines
3.1 KiB
121 lines
3.1 KiB
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
|
|
|