Skip to content

Commit 00282d6

Browse files
authored
core: add helpers for working with paths across OSes (#1102)
1 parent b5f31bb commit 00282d6

File tree

4 files changed

+226
-1
lines changed

4 files changed

+226
-1
lines changed

packages/core/README.md

+24-1
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,27 @@ outputs:
309309
runs:
310310
using: 'node12'
311311
main: 'dist/index.js'
312-
```
312+
```
313+
314+
#### Filesystem path helpers
315+
316+
You can use these methods to manipulate file paths across operating systems.
317+
318+
The `toPosixPath` function converts input paths to Posix-style (Linux) paths.
319+
The `toWin32Path` function converts input paths to Windows-style paths. These
320+
functions work independently of the underlying runner operating system.
321+
322+
```js
323+
toPosixPath('\\foo\\bar') // => /foo/bar
324+
toWin32Path('/foo/bar') // => \foo\bar
325+
```
326+
327+
The `toPlatformPath` function converts input paths to the expected value on the runner's operating system.
328+
329+
```js
330+
// On a Windows runner.
331+
toPlatformPath('/foo/bar') // => \foo\bar
332+
333+
// On a Linux runner.
334+
toPlatformPath('\\foo\\bar') // => /foo/bar
335+
```
+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import * as path from 'path'
2+
3+
import {toPlatformPath, toPosixPath, toWin32Path} from '../src/path-utils'
4+
5+
describe('#toPosixPath', () => {
6+
const cases: {
7+
only?: boolean
8+
name: string
9+
input: string
10+
expected: string
11+
}[] = [
12+
{
13+
name: 'empty string',
14+
input: '',
15+
expected: ''
16+
},
17+
{
18+
name: 'single value',
19+
input: 'foo',
20+
expected: 'foo'
21+
},
22+
{
23+
name: 'with posix relative',
24+
input: 'foo/bar/baz',
25+
expected: 'foo/bar/baz'
26+
},
27+
{
28+
name: 'with posix absolute',
29+
input: '/foo/bar/baz',
30+
expected: '/foo/bar/baz'
31+
},
32+
{
33+
name: 'with win32 relative',
34+
input: 'foo\\bar\\baz',
35+
expected: 'foo/bar/baz'
36+
},
37+
{
38+
name: 'with win32 absolute',
39+
input: '\\foo\\bar\\baz',
40+
expected: '/foo/bar/baz'
41+
},
42+
{
43+
name: 'with a mix',
44+
input: '\\foo/bar/baz',
45+
expected: '/foo/bar/baz'
46+
}
47+
]
48+
49+
for (const tc of cases) {
50+
const fn = tc.only ? it.only : it
51+
fn(tc.name, () => {
52+
const result = toPosixPath(tc.input)
53+
expect(result).toEqual(tc.expected)
54+
})
55+
}
56+
})
57+
58+
describe('#toWin32Path', () => {
59+
const cases: {
60+
only?: boolean
61+
name: string
62+
input: string
63+
expected: string
64+
}[] = [
65+
{
66+
name: 'empty string',
67+
input: '',
68+
expected: ''
69+
},
70+
{
71+
name: 'single value',
72+
input: 'foo',
73+
expected: 'foo'
74+
},
75+
{
76+
name: 'with posix relative',
77+
input: 'foo/bar/baz',
78+
expected: 'foo\\bar\\baz'
79+
},
80+
{
81+
name: 'with posix absolute',
82+
input: '/foo/bar/baz',
83+
expected: '\\foo\\bar\\baz'
84+
},
85+
{
86+
name: 'with win32 relative',
87+
input: 'foo\\bar\\baz',
88+
expected: 'foo\\bar\\baz'
89+
},
90+
{
91+
name: 'with win32 absolute',
92+
input: '\\foo\\bar\\baz',
93+
expected: '\\foo\\bar\\baz'
94+
},
95+
{
96+
name: 'with a mix',
97+
input: '\\foo/bar\\baz',
98+
expected: '\\foo\\bar\\baz'
99+
}
100+
]
101+
102+
for (const tc of cases) {
103+
const fn = tc.only ? it.only : it
104+
fn(tc.name, () => {
105+
const result = toWin32Path(tc.input)
106+
expect(result).toEqual(tc.expected)
107+
})
108+
}
109+
})
110+
111+
describe('#toPlatformPath', () => {
112+
const cases: {
113+
only?: boolean
114+
name: string
115+
input: string
116+
expected: string
117+
}[] = [
118+
{
119+
name: 'empty string',
120+
input: '',
121+
expected: ''
122+
},
123+
{
124+
name: 'single value',
125+
input: 'foo',
126+
expected: 'foo'
127+
},
128+
{
129+
name: 'with posix relative',
130+
input: 'foo/bar/baz',
131+
expected: path.join('foo', 'bar', 'baz')
132+
},
133+
{
134+
name: 'with posix absolute',
135+
input: '/foo/bar/baz',
136+
expected: path.join(path.sep, 'foo', 'bar', 'baz')
137+
},
138+
{
139+
name: 'with win32 relative',
140+
input: 'foo\\bar\\baz',
141+
expected: path.join('foo', 'bar', 'baz')
142+
},
143+
{
144+
name: 'with win32 absolute',
145+
input: '\\foo\\bar\\baz',
146+
expected: path.join(path.sep, 'foo', 'bar', 'baz')
147+
},
148+
{
149+
name: 'with a mix',
150+
input: '\\foo/bar\\baz',
151+
expected: path.join(path.sep, 'foo', 'bar', 'baz')
152+
}
153+
]
154+
155+
for (const tc of cases) {
156+
const fn = tc.only ? it.only : it
157+
fn(tc.name, () => {
158+
const result = toPlatformPath(tc.input)
159+
expect(result).toEqual(tc.expected)
160+
})
161+
}
162+
})

packages/core/src/core.ts

+5
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,8 @@ export {summary} from './summary'
369369
* @deprecated use core.summary
370370
*/
371371
export {markdownSummary} from './summary'
372+
373+
/**
374+
* Path exports
375+
*/
376+
export {toPosixPath, toWin32Path, toPlatformPath} from './path-utils'

packages/core/src/path-utils.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as path from 'path'
2+
3+
/**
4+
* toPosixPath converts the given path to the posix form. On Windows, \\ will be
5+
* replaced with /.
6+
*
7+
* @param pth. Path to transform.
8+
* @return string Posix path.
9+
*/
10+
export function toPosixPath(pth: string): string {
11+
return pth.replace(/[\\]/g, '/')
12+
}
13+
14+
/**
15+
* toWin32Path converts the given path to the win32 form. On Linux, / will be
16+
* replaced with \\.
17+
*
18+
* @param pth. Path to transform.
19+
* @return string Win32 path.
20+
*/
21+
export function toWin32Path(pth: string): string {
22+
return pth.replace(/[/]/g, '\\')
23+
}
24+
25+
/**
26+
* toPlatformPath converts the given path to a platform-specific path. It does
27+
* this by replacing instances of / and \ with the platform-specific path
28+
* separator.
29+
*
30+
* @param pth The path to platformize.
31+
* @return string The platform-specific path.
32+
*/
33+
export function toPlatformPath(pth: string): string {
34+
return pth.replace(/[/\\]/g, path.sep)
35+
}

0 commit comments

Comments
 (0)