123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- // A derivative work based on:
- // <https://github.com/browserify/path-browserify>.
- // Which is licensed:
- //
- // MIT License
- //
- // Copyright (c) 2013 James Halliday
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy of
- // this software and associated documentation files (the "Software"), to deal in
- // the Software without restriction, including without limitation the rights to
- // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
- // the Software, and to permit persons to whom the Software is furnished to do so,
- // subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in all
- // copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
- // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
- // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
- // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- // A derivative work based on:
- //
- // Parts of that are extracted from Node’s internal `path` module:
- // <https://github.com/nodejs/node/blob/master/lib/path.js>.
- // Which is licensed:
- //
- // Copyright Joyent, Inc. and other Node contributors.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a
- // copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to permit
- // persons to whom the Software is furnished to do so, subject to the
- // following conditions:
- //
- // The above copyright notice and this permission notice shall be included
- // in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
- // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- // USE OR OTHER DEALINGS IN THE SOFTWARE.
- export const path = {basename, dirname, extname, join, sep: '/'}
- /* eslint-disable max-depth, complexity */
- /**
- * Get the basename from a path.
- *
- * @param {string} path
- * File path.
- * @param {string | null | undefined} [ext]
- * Extension to strip.
- * @returns {string}
- * Stem or basename.
- */
- function basename(path, ext) {
- if (ext !== undefined && typeof ext !== 'string') {
- throw new TypeError('"ext" argument must be a string')
- }
- assertPath(path)
- let start = 0
- let end = -1
- let index = path.length
- /** @type {boolean | undefined} */
- let seenNonSlash
- if (ext === undefined || ext.length === 0 || ext.length > path.length) {
- while (index--) {
- if (path.codePointAt(index) === 47 /* `/` */) {
- // If we reached a path separator that was not part of a set of path
- // separators at the end of the string, stop now.
- if (seenNonSlash) {
- start = index + 1
- break
- }
- } else if (end < 0) {
- // We saw the first non-path separator, mark this as the end of our
- // path component.
- seenNonSlash = true
- end = index + 1
- }
- }
- return end < 0 ? '' : path.slice(start, end)
- }
- if (ext === path) {
- return ''
- }
- let firstNonSlashEnd = -1
- let extIndex = ext.length - 1
- while (index--) {
- if (path.codePointAt(index) === 47 /* `/` */) {
- // If we reached a path separator that was not part of a set of path
- // separators at the end of the string, stop now.
- if (seenNonSlash) {
- start = index + 1
- break
- }
- } else {
- if (firstNonSlashEnd < 0) {
- // We saw the first non-path separator, remember this index in case
- // we need it if the extension ends up not matching.
- seenNonSlash = true
- firstNonSlashEnd = index + 1
- }
- if (extIndex > -1) {
- // Try to match the explicit extension.
- if (path.codePointAt(index) === ext.codePointAt(extIndex--)) {
- if (extIndex < 0) {
- // We matched the extension, so mark this as the end of our path
- // component
- end = index
- }
- } else {
- // Extension does not match, so our result is the entire path
- // component
- extIndex = -1
- end = firstNonSlashEnd
- }
- }
- }
- }
- if (start === end) {
- end = firstNonSlashEnd
- } else if (end < 0) {
- end = path.length
- }
- return path.slice(start, end)
- }
- /**
- * Get the dirname from a path.
- *
- * @param {string} path
- * File path.
- * @returns {string}
- * File path.
- */
- function dirname(path) {
- assertPath(path)
- if (path.length === 0) {
- return '.'
- }
- let end = -1
- let index = path.length
- /** @type {boolean | undefined} */
- let unmatchedSlash
- // Prefix `--` is important to not run on `0`.
- while (--index) {
- if (path.codePointAt(index) === 47 /* `/` */) {
- if (unmatchedSlash) {
- end = index
- break
- }
- } else if (!unmatchedSlash) {
- // We saw the first non-path separator
- unmatchedSlash = true
- }
- }
- return end < 0
- ? path.codePointAt(0) === 47 /* `/` */
- ? '/'
- : '.'
- : end === 1 && path.codePointAt(0) === 47 /* `/` */
- ? '//'
- : path.slice(0, end)
- }
- /**
- * Get an extname from a path.
- *
- * @param {string} path
- * File path.
- * @returns {string}
- * Extname.
- */
- function extname(path) {
- assertPath(path)
- let index = path.length
- let end = -1
- let startPart = 0
- let startDot = -1
- // Track the state of characters (if any) we see before our first dot and
- // after any path separator we find.
- let preDotState = 0
- /** @type {boolean | undefined} */
- let unmatchedSlash
- while (index--) {
- const code = path.codePointAt(index)
- if (code === 47 /* `/` */) {
- // If we reached a path separator that was not part of a set of path
- // separators at the end of the string, stop now.
- if (unmatchedSlash) {
- startPart = index + 1
- break
- }
- continue
- }
- if (end < 0) {
- // We saw the first non-path separator, mark this as the end of our
- // extension.
- unmatchedSlash = true
- end = index + 1
- }
- if (code === 46 /* `.` */) {
- // If this is our first dot, mark it as the start of our extension.
- if (startDot < 0) {
- startDot = index
- } else if (preDotState !== 1) {
- preDotState = 1
- }
- } else if (startDot > -1) {
- // We saw a non-dot and non-path separator before our dot, so we should
- // have a good chance at having a non-empty extension.
- preDotState = -1
- }
- }
- if (
- startDot < 0 ||
- end < 0 ||
- // We saw a non-dot character immediately before the dot.
- preDotState === 0 ||
- // The (right-most) trimmed path component is exactly `..`.
- (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
- ) {
- return ''
- }
- return path.slice(startDot, end)
- }
- /**
- * Join segments from a path.
- *
- * @param {Array<string>} segments
- * Path segments.
- * @returns {string}
- * File path.
- */
- function join(...segments) {
- let index = -1
- /** @type {string | undefined} */
- let joined
- while (++index < segments.length) {
- assertPath(segments[index])
- if (segments[index]) {
- joined =
- joined === undefined ? segments[index] : joined + '/' + segments[index]
- }
- }
- return joined === undefined ? '.' : normalize(joined)
- }
- /**
- * Normalize a basic file path.
- *
- * @param {string} path
- * File path.
- * @returns {string}
- * File path.
- */
- // Note: `normalize` is not exposed as `path.normalize`, so some code is
- // manually removed from it.
- function normalize(path) {
- assertPath(path)
- const absolute = path.codePointAt(0) === 47 /* `/` */
- // Normalize the path according to POSIX rules.
- let value = normalizeString(path, !absolute)
- if (value.length === 0 && !absolute) {
- value = '.'
- }
- if (value.length > 0 && path.codePointAt(path.length - 1) === 47 /* / */) {
- value += '/'
- }
- return absolute ? '/' + value : value
- }
- /**
- * Resolve `.` and `..` elements in a path with directory names.
- *
- * @param {string} path
- * File path.
- * @param {boolean} allowAboveRoot
- * Whether `..` can move above root.
- * @returns {string}
- * File path.
- */
- function normalizeString(path, allowAboveRoot) {
- let result = ''
- let lastSegmentLength = 0
- let lastSlash = -1
- let dots = 0
- let index = -1
- /** @type {number | undefined} */
- let code
- /** @type {number} */
- let lastSlashIndex
- while (++index <= path.length) {
- if (index < path.length) {
- code = path.codePointAt(index)
- } else if (code === 47 /* `/` */) {
- break
- } else {
- code = 47 /* `/` */
- }
- if (code === 47 /* `/` */) {
- if (lastSlash === index - 1 || dots === 1) {
- // Empty.
- } else if (lastSlash !== index - 1 && dots === 2) {
- if (
- result.length < 2 ||
- lastSegmentLength !== 2 ||
- result.codePointAt(result.length - 1) !== 46 /* `.` */ ||
- result.codePointAt(result.length - 2) !== 46 /* `.` */
- ) {
- if (result.length > 2) {
- lastSlashIndex = result.lastIndexOf('/')
- if (lastSlashIndex !== result.length - 1) {
- if (lastSlashIndex < 0) {
- result = ''
- lastSegmentLength = 0
- } else {
- result = result.slice(0, lastSlashIndex)
- lastSegmentLength = result.length - 1 - result.lastIndexOf('/')
- }
- lastSlash = index
- dots = 0
- continue
- }
- } else if (result.length > 0) {
- result = ''
- lastSegmentLength = 0
- lastSlash = index
- dots = 0
- continue
- }
- }
- if (allowAboveRoot) {
- result = result.length > 0 ? result + '/..' : '..'
- lastSegmentLength = 2
- }
- } else {
- if (result.length > 0) {
- result += '/' + path.slice(lastSlash + 1, index)
- } else {
- result = path.slice(lastSlash + 1, index)
- }
- lastSegmentLength = index - lastSlash - 1
- }
- lastSlash = index
- dots = 0
- } else if (code === 46 /* `.` */ && dots > -1) {
- dots++
- } else {
- dots = -1
- }
- }
- return result
- }
- /**
- * Make sure `path` is a string.
- *
- * @param {string} path
- * File path.
- * @returns {asserts path is string}
- * Nothing.
- */
- function assertPath(path) {
- if (typeof path !== 'string') {
- throw new TypeError(
- 'Path must be a string. Received ' + JSON.stringify(path)
- )
- }
- }
- /* eslint-enable max-depth, complexity */
|