From 00282d61458d99f725588a9463b9d25dfda51c22 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Wed, 15 Jun 2022 08:18:44 -0700 Subject: [PATCH] core: add helpers for working with paths across OSes (#1102) --- packages/core/README.md | 25 +++- packages/core/__tests__/path-utils.test.ts | 162 +++++++++++++++++++++ packages/core/src/core.ts | 5 + packages/core/src/path-utils.ts | 35 +++++ 4 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 packages/core/__tests__/path-utils.test.ts create mode 100644 packages/core/src/path-utils.ts diff --git a/packages/core/README.md b/packages/core/README.md index 8f227a83..3c20c8ea 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -309,4 +309,27 @@ outputs: runs: using: 'node12' main: 'dist/index.js' -``` \ No newline at end of file +``` + +#### Filesystem path helpers + +You can use these methods to manipulate file paths across operating systems. + +The `toPosixPath` function converts input paths to Posix-style (Linux) paths. +The `toWin32Path` function converts input paths to Windows-style paths. These +functions work independently of the underlying runner operating system. + +```js +toPosixPath('\\foo\\bar') // => /foo/bar +toWin32Path('/foo/bar') // => \foo\bar +``` + +The `toPlatformPath` function converts input paths to the expected value on the runner's operating system. + +```js +// On a Windows runner. +toPlatformPath('/foo/bar') // => \foo\bar + +// On a Linux runner. +toPlatformPath('\\foo\\bar') // => /foo/bar +``` diff --git a/packages/core/__tests__/path-utils.test.ts b/packages/core/__tests__/path-utils.test.ts new file mode 100644 index 00000000..fda8a5e7 --- /dev/null +++ b/packages/core/__tests__/path-utils.test.ts @@ -0,0 +1,162 @@ +import * as path from 'path' + +import {toPlatformPath, toPosixPath, toWin32Path} from '../src/path-utils' + +describe('#toPosixPath', () => { + const cases: { + only?: boolean + name: string + input: string + expected: string + }[] = [ + { + name: 'empty string', + input: '', + expected: '' + }, + { + name: 'single value', + input: 'foo', + expected: 'foo' + }, + { + name: 'with posix relative', + input: 'foo/bar/baz', + expected: 'foo/bar/baz' + }, + { + name: 'with posix absolute', + input: '/foo/bar/baz', + expected: '/foo/bar/baz' + }, + { + name: 'with win32 relative', + input: 'foo\\bar\\baz', + expected: 'foo/bar/baz' + }, + { + name: 'with win32 absolute', + input: '\\foo\\bar\\baz', + expected: '/foo/bar/baz' + }, + { + name: 'with a mix', + input: '\\foo/bar/baz', + expected: '/foo/bar/baz' + } + ] + + for (const tc of cases) { + const fn = tc.only ? it.only : it + fn(tc.name, () => { + const result = toPosixPath(tc.input) + expect(result).toEqual(tc.expected) + }) + } +}) + +describe('#toWin32Path', () => { + const cases: { + only?: boolean + name: string + input: string + expected: string + }[] = [ + { + name: 'empty string', + input: '', + expected: '' + }, + { + name: 'single value', + input: 'foo', + expected: 'foo' + }, + { + name: 'with posix relative', + input: 'foo/bar/baz', + expected: 'foo\\bar\\baz' + }, + { + name: 'with posix absolute', + input: '/foo/bar/baz', + expected: '\\foo\\bar\\baz' + }, + { + name: 'with win32 relative', + input: 'foo\\bar\\baz', + expected: 'foo\\bar\\baz' + }, + { + name: 'with win32 absolute', + input: '\\foo\\bar\\baz', + expected: '\\foo\\bar\\baz' + }, + { + name: 'with a mix', + input: '\\foo/bar\\baz', + expected: '\\foo\\bar\\baz' + } + ] + + for (const tc of cases) { + const fn = tc.only ? it.only : it + fn(tc.name, () => { + const result = toWin32Path(tc.input) + expect(result).toEqual(tc.expected) + }) + } +}) + +describe('#toPlatformPath', () => { + const cases: { + only?: boolean + name: string + input: string + expected: string + }[] = [ + { + name: 'empty string', + input: '', + expected: '' + }, + { + name: 'single value', + input: 'foo', + expected: 'foo' + }, + { + name: 'with posix relative', + input: 'foo/bar/baz', + expected: path.join('foo', 'bar', 'baz') + }, + { + name: 'with posix absolute', + input: '/foo/bar/baz', + expected: path.join(path.sep, 'foo', 'bar', 'baz') + }, + { + name: 'with win32 relative', + input: 'foo\\bar\\baz', + expected: path.join('foo', 'bar', 'baz') + }, + { + name: 'with win32 absolute', + input: '\\foo\\bar\\baz', + expected: path.join(path.sep, 'foo', 'bar', 'baz') + }, + { + name: 'with a mix', + input: '\\foo/bar\\baz', + expected: path.join(path.sep, 'foo', 'bar', 'baz') + } + ] + + for (const tc of cases) { + const fn = tc.only ? it.only : it + fn(tc.name, () => { + const result = toPlatformPath(tc.input) + expect(result).toEqual(tc.expected) + }) + } +}) diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index f5e7941b..e4f8bf95 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -369,3 +369,8 @@ export {summary} from './summary' * @deprecated use core.summary */ export {markdownSummary} from './summary' + +/** + * Path exports + */ +export {toPosixPath, toWin32Path, toPlatformPath} from './path-utils' diff --git a/packages/core/src/path-utils.ts b/packages/core/src/path-utils.ts new file mode 100644 index 00000000..f3085377 --- /dev/null +++ b/packages/core/src/path-utils.ts @@ -0,0 +1,35 @@ +import * as path from 'path' + +/** + * toPosixPath converts the given path to the posix form. On Windows, \\ will be + * replaced with /. + * + * @param pth. Path to transform. + * @return string Posix path. + */ +export function toPosixPath(pth: string): string { + return pth.replace(/[\\]/g, '/') +} + +/** + * toWin32Path converts the given path to the win32 form. On Linux, / will be + * replaced with \\. + * + * @param pth. Path to transform. + * @return string Win32 path. + */ +export function toWin32Path(pth: string): string { + return pth.replace(/[/]/g, '\\') +} + +/** + * toPlatformPath converts the given path to a platform-specific path. It does + * this by replacing instances of / and \ with the platform-specific path + * separator. + * + * @param pth The path to platformize. + * @return string The platform-specific path. + */ +export function toPlatformPath(pth: string): string { + return pth.replace(/[/\\]/g, path.sep) +}