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.
221 lines
5.0 KiB
221 lines
5.0 KiB
/* |
|
MIT License http://www.opensource.org/licenses/mit-license.php |
|
Author Tobias Koppers @sokra |
|
*/ |
|
|
|
"use strict"; |
|
|
|
const path = require("path"); |
|
|
|
const CHAR_HASH = "#".charCodeAt(0); |
|
const CHAR_SLASH = "/".charCodeAt(0); |
|
const CHAR_BACKSLASH = "\\".charCodeAt(0); |
|
const CHAR_A = "A".charCodeAt(0); |
|
const CHAR_Z = "Z".charCodeAt(0); |
|
const CHAR_LOWER_A = "a".charCodeAt(0); |
|
const CHAR_LOWER_Z = "z".charCodeAt(0); |
|
const CHAR_DOT = ".".charCodeAt(0); |
|
const CHAR_COLON = ":".charCodeAt(0); |
|
|
|
const posixNormalize = path.posix.normalize; |
|
const winNormalize = path.win32.normalize; |
|
|
|
/** |
|
* @enum {number} |
|
*/ |
|
const PathType = Object.freeze({ |
|
Empty: 0, |
|
Normal: 1, |
|
Relative: 2, |
|
AbsoluteWin: 3, |
|
AbsolutePosix: 4, |
|
Internal: 5 |
|
}); |
|
exports.PathType = PathType; |
|
|
|
/** |
|
* @param {string} p a path |
|
* @returns {PathType} type of path |
|
*/ |
|
const getType = p => { |
|
switch (p.length) { |
|
case 0: |
|
return PathType.Empty; |
|
case 1: { |
|
const c0 = p.charCodeAt(0); |
|
switch (c0) { |
|
case CHAR_DOT: |
|
return PathType.Relative; |
|
case CHAR_SLASH: |
|
return PathType.AbsolutePosix; |
|
case CHAR_HASH: |
|
return PathType.Internal; |
|
} |
|
return PathType.Normal; |
|
} |
|
case 2: { |
|
const c0 = p.charCodeAt(0); |
|
switch (c0) { |
|
case CHAR_DOT: { |
|
const c1 = p.charCodeAt(1); |
|
switch (c1) { |
|
case CHAR_DOT: |
|
case CHAR_SLASH: |
|
return PathType.Relative; |
|
} |
|
return PathType.Normal; |
|
} |
|
case CHAR_SLASH: |
|
return PathType.AbsolutePosix; |
|
case CHAR_HASH: |
|
return PathType.Internal; |
|
} |
|
const c1 = p.charCodeAt(1); |
|
if (c1 === CHAR_COLON) { |
|
if ( |
|
(c0 >= CHAR_A && c0 <= CHAR_Z) || |
|
(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z) |
|
) { |
|
return PathType.AbsoluteWin; |
|
} |
|
} |
|
return PathType.Normal; |
|
} |
|
} |
|
const c0 = p.charCodeAt(0); |
|
switch (c0) { |
|
case CHAR_DOT: { |
|
const c1 = p.charCodeAt(1); |
|
switch (c1) { |
|
case CHAR_SLASH: |
|
return PathType.Relative; |
|
case CHAR_DOT: { |
|
const c2 = p.charCodeAt(2); |
|
if (c2 === CHAR_SLASH) return PathType.Relative; |
|
return PathType.Normal; |
|
} |
|
} |
|
return PathType.Normal; |
|
} |
|
case CHAR_SLASH: |
|
return PathType.AbsolutePosix; |
|
case CHAR_HASH: |
|
return PathType.Internal; |
|
} |
|
const c1 = p.charCodeAt(1); |
|
if (c1 === CHAR_COLON) { |
|
const c2 = p.charCodeAt(2); |
|
if ( |
|
(c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) && |
|
((c0 >= CHAR_A && c0 <= CHAR_Z) || |
|
(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z)) |
|
) { |
|
return PathType.AbsoluteWin; |
|
} |
|
} |
|
return PathType.Normal; |
|
}; |
|
exports.getType = getType; |
|
|
|
/** |
|
* @param {string} p a path |
|
* @returns {string} the normalized path |
|
*/ |
|
const normalize = p => { |
|
switch (getType(p)) { |
|
case PathType.Empty: |
|
return p; |
|
case PathType.AbsoluteWin: |
|
return winNormalize(p); |
|
case PathType.Relative: { |
|
const r = posixNormalize(p); |
|
return getType(r) === PathType.Relative ? r : `./${r}`; |
|
} |
|
} |
|
return posixNormalize(p); |
|
}; |
|
exports.normalize = normalize; |
|
|
|
/** |
|
* @param {string} rootPath the root path |
|
* @param {string | undefined} request the request path |
|
* @returns {string} the joined path |
|
*/ |
|
const join = (rootPath, request) => { |
|
if (!request) return normalize(rootPath); |
|
const requestType = getType(request); |
|
switch (requestType) { |
|
case PathType.AbsolutePosix: |
|
return posixNormalize(request); |
|
case PathType.AbsoluteWin: |
|
return winNormalize(request); |
|
} |
|
switch (getType(rootPath)) { |
|
case PathType.Normal: |
|
case PathType.Relative: |
|
case PathType.AbsolutePosix: |
|
return posixNormalize(`${rootPath}/${request}`); |
|
case PathType.AbsoluteWin: |
|
return winNormalize(`${rootPath}\\${request}`); |
|
} |
|
switch (requestType) { |
|
case PathType.Empty: |
|
return rootPath; |
|
case PathType.Relative: { |
|
const r = posixNormalize(rootPath); |
|
return getType(r) === PathType.Relative ? r : `./${r}`; |
|
} |
|
} |
|
return posixNormalize(rootPath); |
|
}; |
|
exports.join = join; |
|
|
|
const joinCache = new Map(); |
|
|
|
/** |
|
* @param {string} rootPath the root path |
|
* @param {string | undefined} request the request path |
|
* @returns {string} the joined path |
|
*/ |
|
const cachedJoin = (rootPath, request) => { |
|
let cacheEntry; |
|
let cache = joinCache.get(rootPath); |
|
if (cache === undefined) { |
|
joinCache.set(rootPath, (cache = new Map())); |
|
} else { |
|
cacheEntry = cache.get(request); |
|
if (cacheEntry !== undefined) return cacheEntry; |
|
} |
|
cacheEntry = join(rootPath, request); |
|
cache.set(request, cacheEntry); |
|
return cacheEntry; |
|
}; |
|
exports.cachedJoin = cachedJoin; |
|
|
|
const checkExportsFieldTarget = relativePath => { |
|
let lastNonSlashIndex = 2; |
|
let slashIndex = relativePath.indexOf("/", 2); |
|
let cd = 0; |
|
|
|
while (slashIndex !== -1) { |
|
const folder = relativePath.slice(lastNonSlashIndex, slashIndex); |
|
|
|
switch (folder) { |
|
case "..": { |
|
cd--; |
|
if (cd < 0) |
|
return new Error( |
|
`Trying to access out of package scope. Requesting ${relativePath}` |
|
); |
|
break; |
|
} |
|
default: |
|
cd++; |
|
break; |
|
} |
|
|
|
lastNonSlashIndex = slashIndex + 1; |
|
slashIndex = relativePath.indexOf("/", lastNonSlashIndex); |
|
} |
|
}; |
|
exports.checkExportsFieldTarget = checkExportsFieldTarget;
|
|
|