Skip to content

Commit 5f29691

Browse files
authored
Merge branch 'main' into bump-mocha
2 parents 0a6037c + 0c9ca96 commit 5f29691

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+705
-204
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
2727
- [Vis Builder] Change classname prefix wiz to vb ([#2581](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2581/files))
2828
- [Vis Builder] Change wizard to vis_builder in file names and paths ([#2587](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2587))
2929
- [Windows] Facilitate building and running OSD and plugins on Windows platforms ([#2601](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2601))
30+
- [Windows] Add `@osd/cross-platform` package to standardize path handling across platforms ([#2703](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2703))
3031
- [Multi DataSource] Address UX comments on Data source list and create page ([#2625](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2625))
3132
- [Vis Builder] Rename wizard to visBuilder in i18n id and formatted message id ([#2635](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2635))
3233
- [Vis Builder] Rename wizard to visBuilder in class name, type name and function name ([#2639](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2639))

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@
138138
"@osd/apm-config-loader": "1.0.0",
139139
"@osd/config": "1.0.0",
140140
"@osd/config-schema": "1.0.0",
141+
"@osd/cross-platform": "1.0.0",
141142
"@osd/i18n": "1.0.0",
142143
"@osd/interpreter": "1.0.0",
143144
"@osd/logging": "1.0.0",

packages/osd-config-schema/package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
"osd:bootstrap": "yarn build"
1111
},
1212
"devDependencies": {
13-
"typescript": "4.0.2",
14-
"tsd": "^0.21.0"
13+
"@osd/cross-platform": "1.0.0",
14+
"tsd": "^0.21.0",
15+
"typescript": "4.0.2"
1516
},
1617
"peerDependencies": {
1718
"lodash": "^4.17.21",

packages/osd-config-schema/src/errors/schema_error.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import { relative, sep } from 'path';
3232
import { SchemaError } from '.';
3333

34+
import { standardize, getRepoRoot } from '@osd/cross-platform';
35+
3436
/**
3537
* Make all paths in stacktrace relative.
3638
*/
@@ -46,9 +48,7 @@ export const cleanStack = (stack: string) =>
4648
}
4749

4850
const path = parts[1];
49-
// Cannot use `standardize` from `@osd/utils
50-
let relativePath = relative(process.cwd(), path);
51-
if (process.platform === 'win32') relativePath = relativePath.replace(/\\/g, '/');
51+
const relativePath = standardize(relative(getRepoRoot(path) || '.', path));
5252

5353
return line.replace(path, relativePath);
5454
})

packages/osd-cross-platform/README.md

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# `@osd/cross-platform` — OpenSearch Dashboards cross-platform helpers
2+
3+
This package contains the helpers to work around the differences across platforms, such as the difference in the path segment separator and the possibility of referencing a path using the short 8.3 name (SFN), a long name, and a long UNC on Windows.
4+
5+
Some helpers are functions that `standardize` the reference to a path or help `getRepoRoot`, and some are constants referencing the `PROCESS_WORKING_DIR` or `REPO_ROOT`.
6+
7+
### Example
8+
9+
When the relative reference of `path` to the working directory is needed, using the code below would produce different results on Linux that it would on Windows and if the process was started in a Windows shell that used short paths, the results differ from a Windows shell that used long paths.
10+
```js
11+
import { relative } from 'path';
12+
13+
const relativePath = relative(process.cwd(), path);
14+
15+
// Output on Linux: relative-path/to/a/file
16+
// Windows: relative-path\to\a\file
17+
// Windows SFN: RELATI~1\to\a\file
18+
```
19+
20+
To avoid those differences, helper functions and constants can be used:
21+
```js
22+
import { relative } from 'path';
23+
import { standardize, PROCESS_WORKING_DIR } from '@osd/cross-platform';
24+
25+
const relativePath = standardize(relative(PROCESS_WORKING_DIR, path));
26+
27+
// Output: relative-path/to/a/file
28+
```
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "@osd/cross-platform",
3+
"main": "./target/index.js",
4+
"version": "1.0.0",
5+
"license": "Apache-2.0",
6+
"private": true,
7+
"scripts": {
8+
"build": "tsc",
9+
"osd:bootstrap": "yarn build"
10+
},
11+
"devDependencies": {
12+
"typescript": "4.0.2",
13+
"tsd": "^0.21.0"
14+
}
15+
}

packages/osd-cross-platform/src/__snapshots__/path.test.ts.snap

+13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
export * from './path';
7+
export * from './process';
8+
export * from './repo_root';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import path from 'path';
7+
import fs from 'fs';
8+
import { access, rmdir, mkdir, writeFile, symlink } from 'fs/promises';
9+
10+
import {
11+
resolveToFullNameSync,
12+
resolveToFullPathSync,
13+
resolveToShortNameSync,
14+
resolveToShortPathSync,
15+
shortNamesSupportedSync,
16+
realPathSync,
17+
realShortPathSync,
18+
standardize,
19+
} from './path';
20+
21+
const tmpTestFolder = './__test_artifacts__';
22+
const longFolderName = '.long-folder-name';
23+
const longFileName = '.long-file-name.txt';
24+
const longSymlinkName = '.sym.link';
25+
const shortFolderName = 'LONG-F~1';
26+
const shortFileName = 'LONG-F~1.TXT';
27+
const dummyWindowsPath = 'C:\\a\\b\\c';
28+
const dummyWindowsPOSIXPath = 'C:/a/b/c';
29+
const dummyPOSIXPath = '/a/b/c';
30+
31+
const onWindows = process.platform === 'win32' ? describe : xdescribe;
32+
const onWindowsWithShortNames = shortNamesSupportedSync() ? describe : xdescribe;
33+
34+
// Save the real process.platform
35+
const realPlatform = Object.getOwnPropertyDescriptor(process, 'platform')!;
36+
37+
describe('Cross Platform', () => {
38+
describe('path', () => {
39+
onWindows('on Windows', () => {
40+
onWindowsWithShortNames('when 8.3 is supported', () => {
41+
beforeAll(async () => {
42+
// Cleanup
43+
try {
44+
// If leftover artifacts were found, get rid of them
45+
await access(tmpTestFolder);
46+
await rmdir(tmpTestFolder, { recursive: true });
47+
} catch (ex) {
48+
// Do nothing; if `rmdir` failed, let the `mkdir` below throw the error
49+
}
50+
51+
await mkdir(tmpTestFolder);
52+
await mkdir(path.resolve(tmpTestFolder, longFolderName));
53+
await writeFile(path.resolve(tmpTestFolder, longFolderName, longFileName), '');
54+
await symlink(
55+
path.resolve(tmpTestFolder, longFolderName),
56+
path.resolve(tmpTestFolder, longSymlinkName),
57+
'junction'
58+
);
59+
});
60+
61+
afterAll(async () => {
62+
try {
63+
await rmdir(tmpTestFolder, { recursive: true });
64+
} catch (ex) {
65+
// Do nothing
66+
}
67+
});
68+
69+
it('can synchronously extract full name of a folder', () => {
70+
const name = path.basename(
71+
resolveToFullPathSync(path.resolve(tmpTestFolder, shortFolderName))
72+
);
73+
expect(name).toBe(longFolderName);
74+
});
75+
76+
it('can synchronously extract full name of a file', () => {
77+
const name = path.basename(
78+
resolveToFullNameSync(path.resolve(tmpTestFolder, shortFolderName, shortFileName))
79+
);
80+
expect(name).toBe(longFileName);
81+
});
82+
83+
it('can synchronously extract short name of a folder', () => {
84+
const name = path.basename(
85+
resolveToShortPathSync(path.resolve(tmpTestFolder, longFolderName))
86+
);
87+
expect(name).toBe(shortFolderName);
88+
});
89+
90+
it('can synchronously extract short name of a file', () => {
91+
const name = path.basename(
92+
resolveToShortNameSync(path.resolve(tmpTestFolder, longFolderName, longFileName))
93+
);
94+
expect(name).toBe(shortFileName);
95+
});
96+
97+
it('can synchronously extract full name of a symbolic link', () => {
98+
const name = path.basename(realPathSync(path.resolve(tmpTestFolder, longSymlinkName)));
99+
expect(name).toBe(longFolderName);
100+
});
101+
102+
it('can synchronously extract short name of a symbolic link', () => {
103+
const name = path.basename(
104+
realShortPathSync(path.resolve(tmpTestFolder, longSymlinkName))
105+
);
106+
expect(name).toBe(shortFolderName);
107+
});
108+
});
109+
});
110+
111+
describe('on platforms other than Windows', () => {
112+
let mockPathNormalize: jest.SpyInstance<string, [p: string]>;
113+
let mockPathResolve: jest.SpyInstance<string, string[]>;
114+
let mockFSRealPathSync: jest.SpyInstance<string>;
115+
116+
beforeAll(() => {
117+
Object.defineProperty(process, 'platform', {
118+
...Object.getOwnPropertyDescriptor(process, 'property'),
119+
value: 'linux',
120+
});
121+
122+
mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyPOSIXPath);
123+
mockPathResolve = jest.spyOn(path, 'resolve').mockReturnValue(dummyPOSIXPath);
124+
mockFSRealPathSync = jest
125+
.spyOn(fs, 'realpathSync')
126+
.mockReturnValue(dummyPOSIXPath) as jest.SpyInstance<string>;
127+
});
128+
129+
afterAll(() => {
130+
// Restore the real property value after each test
131+
Object.defineProperty(process, 'platform', realPlatform);
132+
mockPathNormalize.mockRestore();
133+
mockPathResolve.mockRestore();
134+
mockFSRealPathSync.mockRestore();
135+
});
136+
137+
it('all short and full name methods return just the normalized paths', () => {
138+
expect(shortNamesSupportedSync()).toBe(false);
139+
expect(resolveToFullPathSync(dummyPOSIXPath)).toBe(dummyPOSIXPath);
140+
expect(resolveToShortPathSync(dummyPOSIXPath)).toBe(dummyPOSIXPath);
141+
});
142+
});
143+
144+
describe('standardize', () => {
145+
describe('on Windows', () => {
146+
let mockPathNormalize: jest.SpyInstance<string, [p: string]>;
147+
148+
beforeAll(() => {
149+
Object.defineProperty(process, 'platform', {
150+
...Object.getOwnPropertyDescriptor(process, 'property'),
151+
value: 'win32',
152+
});
153+
154+
mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyWindowsPath);
155+
});
156+
157+
afterAll(() => {
158+
// Restore the real property value after each test
159+
Object.defineProperty(process, 'platform', realPlatform);
160+
mockPathNormalize.mockRestore();
161+
});
162+
163+
it('produces a path in native format', () => {
164+
expect(standardize(dummyWindowsPath, false, false)).toMatchSnapshot();
165+
});
166+
167+
it('produces a path in native format even for POSIX input', () => {
168+
expect(standardize(dummyWindowsPOSIXPath, false, false)).toMatchSnapshot();
169+
});
170+
171+
it('produces a path in native format with escaped backslashes', () => {
172+
expect(standardize(dummyWindowsPath, false, true)).toMatchSnapshot();
173+
});
174+
175+
it('produces a path in POSIX format', () => {
176+
expect(standardize(dummyWindowsPath)).toMatchSnapshot();
177+
});
178+
});
179+
180+
describe('on POSIX-compatible platforms', () => {
181+
let mockPathNormalize: jest.SpyInstance<string, [p: string]>;
182+
183+
beforeAll(() => {
184+
Object.defineProperty(process, 'platform', {
185+
...Object.getOwnPropertyDescriptor(process, 'property'),
186+
value: 'linux',
187+
});
188+
189+
mockPathNormalize = jest.spyOn(path, 'normalize').mockReturnValue(dummyPOSIXPath);
190+
});
191+
192+
afterAll(() => {
193+
// Restore the real property value after each test
194+
Object.defineProperty(process, 'platform', realPlatform);
195+
mockPathNormalize.mockRestore();
196+
});
197+
198+
it('produces a path in POSIX format', () => {
199+
expect(standardize(dummyPOSIXPath)).toMatchSnapshot();
200+
});
201+
202+
it('ignores additional parameters', () => {
203+
expect(standardize(dummyPOSIXPath, false, true)).toMatchSnapshot();
204+
});
205+
});
206+
});
207+
});
208+
});

0 commit comments

Comments
 (0)