Skip to content

Commit

Permalink
feat(lib): toNamespacedPath
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <[email protected]>
  • Loading branch information
unicornware committed Dec 12, 2022
1 parent e3aeab3 commit 6a8e59c
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 0 deletions.
63 changes: 63 additions & 0 deletions src/lib/__tests__/to-namespaced-path.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @file Unit Tests - toNamespacedPath
* @module pathe/lib/tests/unit/toNamespacedPath
* @see https://github.com/nodejs/node/blob/main/test/parallel/test-path-makelong.js
*/

import sep from '#src/lib/sep'
import { posix, win32 } from 'node:path'
import testSubject from '../to-namespaced-path'

describe('unit:lib/toNamespacedPath', () => {
it('should return path if path is not drive path or unc path', () => {
// Arrange
const cases: Parameters<typeof testSubject>[] = [
[''],
['/foo/bar'],
['foo/bar'],
[null as unknown as string]
]

// Act + Expect
cases.forEach(([path]) => {
expect(testSubject(path)).to.equal(posix.toNamespacedPath(path))
})
})

describe('windows', () => {
beforeAll(() => {
Object.assign(process.env, { '=P:': 'P:' + process.cwd() })
})

/**
* Converts Windows-style path separators (`\`) to POSIX (`/`).
*
* @param {string} path - Path to normalize
* @return {string} `path` normalized
*/
const ensurePosix = (path: string): string => path.replace(/\\/g, sep)

it('should return namespace-prefixed path', () => {
// Arrange
const cases: Parameters<typeof testSubject>[] = [
['C:'],
['C:\\file.txt'],
['P:'],
['P:\\file.txt'],
['\\\\.\\pipe\\somepipe'],
['\\\\?\\UNC\\someserver\\someshare\\somefile'],
['\\\\?\\foo'],
['\\\\file.txt'],
['\\\\foo\\bar'],
['\\\\someserver\\someshare\\somefile'],
['\\file.txt'],
['file.txt']
]

// Act + Expect
cases.forEach(([p]) => {
expect(testSubject(p)).to.equal(ensurePosix(win32.toNamespacedPath(p)))
})
})
})
})
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export { default as normalize } from './normalize'
export { default as parse } from './parse'
export { default as resolve } from './resolve'
export { default as sep } from './sep'
export { default as toNamespacedPath } from './to-namespaced-path'
54 changes: 54 additions & 0 deletions src/lib/to-namespaced-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* @file Library - toNamespacedPath
* @module pathe/lib/toNamespacedPath
*/

import { DOT } from '#src/internal/constants'
import ensurePosix from '#src/internal/ensure-posix'
import isDrivePath from '#src/internal/is-drive-path'
import isUncPath from '#src/internal/is-unc-path'
import isAbsolute from './is-absolute'
import resolve from './resolve'
import sep from './sep'

/**
* Returns an equivalent [namespace-prefixed path][1] for the given `path`.
*
* If the given `path` isn't a [drive path][2] or [UNC path][3], the path will
* be returned without modifications.
*
* [1]: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#namespaces
* [2]: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
* [3]: https://learn.microsoft.com/dotnet/standard/io/file-path-formats#unc-paths
*
* @param {string} path - Path to evaluate
* @return {string} `path` without modification or as namespace-prefixed path
*/
const toNamespacedPath = (path: string): string => {
// exit early if path is not a string
if (typeof path !== 'string' || !path) return path

// ensure path meets posix standards
path = ensurePosix(path)

/**
* Fully resolved {@linkcode path}.
*
* @const {string} resolved
*/
const resolved: string = resolve(path)

// matched device root => convert path to long unc path
if (isDrivePath(resolved) && isAbsolute(resolved)) {
return `${sep.repeat(2)}?${sep}${resolved}`
}

// matched non-long unc root => convert the path to long unc path
if (isUncPath(resolved) && !['?', DOT].includes(resolved.charAt(2))) {
return `${sep.repeat(2)}?${sep}UNC${sep}${resolved.slice(2)}`
}

return path
}

export default toNamespacedPath

0 comments on commit 6a8e59c

Please sign in to comment.