@opensumi/ide-utils 2.21.13 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/lib/arrays.d.ts +3 -3
  2. package/lib/arrays.d.ts.map +1 -1
  3. package/lib/arrays.js.map +1 -1
  4. package/lib/async.d.ts +3 -3
  5. package/lib/async.d.ts.map +1 -1
  6. package/lib/buffer.js +4 -4
  7. package/lib/buffer.js.map +1 -1
  8. package/lib/character-classifier.js +3 -3
  9. package/lib/character-classifier.js.map +1 -1
  10. package/lib/decorators.d.ts +1 -1
  11. package/lib/decorators.d.ts.map +1 -1
  12. package/lib/encoding.d.ts +7 -1
  13. package/lib/encoding.d.ts.map +1 -1
  14. package/lib/encoding.js +15 -2
  15. package/lib/encoding.js.map +1 -1
  16. package/lib/errors.d.ts +2 -2
  17. package/lib/errors.d.ts.map +1 -1
  18. package/lib/event.d.ts +2 -2
  19. package/lib/event.d.ts.map +1 -1
  20. package/lib/filters.d.ts +3 -3
  21. package/lib/filters.d.ts.map +1 -1
  22. package/lib/filters.js +25 -25
  23. package/lib/filters.js.map +1 -1
  24. package/lib/glob.d.ts +2 -2
  25. package/lib/glob.d.ts.map +1 -1
  26. package/lib/glob.js +1 -1
  27. package/lib/glob.js.map +1 -1
  28. package/lib/hash.js +10 -10
  29. package/lib/hash.js.map +1 -1
  30. package/lib/iterator.d.ts +1 -1
  31. package/lib/iterator.d.ts.map +1 -1
  32. package/lib/linked-text.d.ts +1 -1
  33. package/lib/linked-text.d.ts.map +1 -1
  34. package/lib/linked-text.js.map +1 -1
  35. package/lib/map.js +17 -17
  36. package/lib/map.js.map +1 -1
  37. package/lib/objects.d.ts +1 -0
  38. package/lib/objects.d.ts.map +1 -1
  39. package/lib/objects.js +38 -1
  40. package/lib/objects.js.map +1 -1
  41. package/lib/os.d.ts +1 -8
  42. package/lib/os.d.ts.map +1 -1
  43. package/lib/os.js +4 -8
  44. package/lib/os.js.map +1 -1
  45. package/lib/path.d.ts +1 -1
  46. package/lib/path.d.ts.map +1 -1
  47. package/lib/path.js +29 -29
  48. package/lib/path.js.map +1 -1
  49. package/lib/platform.js +8 -8
  50. package/lib/platform.js.map +1 -1
  51. package/lib/process.d.ts.map +1 -1
  52. package/lib/process.js +28 -13
  53. package/lib/process.js.map +1 -1
  54. package/lib/progress.js +3 -3
  55. package/lib/progress.js.map +1 -1
  56. package/lib/strings.js +9 -9
  57. package/lib/strings.js.map +1 -1
  58. package/lib/types.d.ts +5 -1
  59. package/lib/types.d.ts.map +1 -1
  60. package/lib/types.js +8 -1
  61. package/lib/types.js.map +1 -1
  62. package/lib/uint.js +4 -4
  63. package/lib/uint.js.map +1 -1
  64. package/lib/uri.js +8 -8
  65. package/lib/uri.js.map +1 -1
  66. package/lib/uuid.d.ts +1 -1
  67. package/lib/uuid.d.ts.map +1 -1
  68. package/lib/uuid.js +3 -2
  69. package/lib/uuid.js.map +1 -1
  70. package/package.json +8 -6
  71. package/src/ansi.ts +10 -0
  72. package/src/argv.ts +57 -0
  73. package/src/arrays.ts +328 -0
  74. package/src/async.ts +580 -0
  75. package/src/buffer.ts +290 -0
  76. package/src/cache.ts +37 -0
  77. package/src/cancellation.ts +136 -0
  78. package/src/charCode.ts +425 -0
  79. package/src/character-classifier.ts +82 -0
  80. package/src/const/encoding.ts +242 -0
  81. package/src/const/index.ts +1 -0
  82. package/src/date.ts +36 -0
  83. package/src/decorators.ts +166 -0
  84. package/src/disposable.ts +377 -0
  85. package/src/encoding.ts +278 -0
  86. package/src/errors.ts +192 -0
  87. package/src/event.ts +1024 -0
  88. package/src/file-uri.ts +41 -0
  89. package/src/filters.ts +887 -0
  90. package/src/functional.ts +43 -0
  91. package/src/glob.ts +752 -0
  92. package/src/hash.ts +336 -0
  93. package/src/iconLabels.ts +149 -0
  94. package/src/index.ts +40 -0
  95. package/src/iterator.ts +36 -0
  96. package/src/lifecycle.ts +48 -0
  97. package/src/linked-list.ts +144 -0
  98. package/src/linked-text.ts +55 -0
  99. package/src/lru-map.ts +133 -0
  100. package/src/map.ts +934 -0
  101. package/src/marshalling.ts +62 -0
  102. package/src/objects.ts +145 -0
  103. package/src/os.ts +76 -0
  104. package/src/path.ts +1885 -0
  105. package/src/platform.ts +194 -0
  106. package/src/process.ts +43 -0
  107. package/src/progress.ts +83 -0
  108. package/src/promises.ts +23 -0
  109. package/src/sequence.ts +28 -0
  110. package/src/strings.ts +1002 -0
  111. package/src/types.ts +214 -0
  112. package/src/uint.ts +59 -0
  113. package/src/uri.ts +298 -0
  114. package/src/uuid.ts +6 -0
package/src/path.ts ADDED
@@ -0,0 +1,1885 @@
1
+ import { isWindows } from './platform';
2
+ import * as process from './process';
3
+
4
+ const SystemPathSeparatorRegex = isWindows ? /\\/g : /\//g;
5
+ /**
6
+ * On POSIX:
7
+ * ┌──────────────────────┬────────────┐
8
+ * │ dir │ base │
9
+ * ├──────┬ ├──────┬─────┤
10
+ * │ root │ │ name │ ext │
11
+ * " / home/user/dir / file .txt "
12
+ * └──────┴───────────────┴──────┴─────┘
13
+ *
14
+ * On Windows:
15
+ * ┌──────────────────────┬────────────┐
16
+ * │ dir │ base │
17
+ * ├──────┬ ├──────┬─────┤
18
+ * │ root │ │ name │ ext │
19
+ * " /c: / home/user/dir / file .txt "
20
+ * └──────┴───────────────┴──────┴─────┘
21
+ */
22
+ export class Path {
23
+ static separator: '/' = '/' as const;
24
+
25
+ static nativeSeparator = isWindows ? '\\' : '/';
26
+
27
+ static isDrive(segment: string): boolean {
28
+ return segment.endsWith(':');
29
+ }
30
+
31
+ static splitPath(path: string): string[] {
32
+ return path.split(Path.separator).filter((path) => !!path);
33
+ }
34
+
35
+ static isRelative(path: string): boolean {
36
+ return !path.startsWith(Path.separator);
37
+ }
38
+
39
+ static pathDepth(path: string): number {
40
+ return path.split(Path.separator).length;
41
+ }
42
+ /**
43
+ * vscode-uri always normalizes drive letters to lower case:
44
+ * https://github.com/Microsoft/vscode-uri/blob/b1d3221579f97f28a839b6f996d76fc45e9964d8/src/index.ts#L1025
45
+ * Theia path should be adjusted to this.
46
+ */
47
+ static normalizeDrive(path: string): string {
48
+ // lower-case windows drive letters in /C:/fff or C:/fff
49
+ if (path.length >= 3 && path.charCodeAt(0) === 47 /* '/' */ && path.charCodeAt(2) === 58 /* ':' */) {
50
+ const code = path.charCodeAt(1);
51
+ if (code >= 65 /* A */ && code <= 90 /* Z */) {
52
+ path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3
53
+ }
54
+ } else if (path.length >= 2 && path.charCodeAt(1) === 58 /* ':' */) {
55
+ const code = path.charCodeAt(0);
56
+ if (code >= 65 /* A */ && code <= 90 /* Z */) {
57
+ path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3
58
+ }
59
+ }
60
+ return path;
61
+ }
62
+
63
+ readonly isAbsolute: boolean;
64
+ readonly isRoot: boolean;
65
+ readonly root: Path | undefined;
66
+ readonly base: string;
67
+ readonly name: string;
68
+ readonly ext: string;
69
+
70
+ private _dir!: Path;
71
+ private readonly raw: string;
72
+
73
+ /**
74
+ * The raw should be normalized, meaning that only '/' is allowed as a path separator.
75
+ */
76
+ constructor(raw: string) {
77
+ raw = raw.replace(SystemPathSeparatorRegex, Path.separator);
78
+ this.raw = Path.normalizeDrive(raw);
79
+ const firstIndex = raw.indexOf(Path.separator);
80
+ const lastIndex = raw.lastIndexOf(Path.separator);
81
+ this.isAbsolute = firstIndex === 0;
82
+ this.base = lastIndex === -1 ? raw : raw.substr(lastIndex + 1);
83
+ this.isRoot = this.isAbsolute && firstIndex === lastIndex && (!this.base || Path.isDrive(this.base));
84
+ this.root = this.computeRoot();
85
+
86
+ const extIndex = this.base.lastIndexOf('.');
87
+ // 处理无后缀文件或者 dot 打头的无后缀文件
88
+ // file like 'a/b/c/test'
89
+ if (extIndex === -1) {
90
+ this.name = this.base;
91
+ this.ext = '';
92
+ } else if (extIndex === 0) {
93
+ // dot file like `a/b/c/.eslintrc`
94
+ this.name = this.base;
95
+ this.ext = '';
96
+ } else {
97
+ this.name = this.base.substr(0, extIndex);
98
+ this.ext = this.base.substr(extIndex);
99
+ }
100
+ }
101
+
102
+ protected computeRoot(): Path | undefined {
103
+ // '/' -> '/'
104
+ // '/c:' -> '/c:'
105
+ if (this.isRoot) {
106
+ return this;
107
+ }
108
+ // 'foo/bar' -> `undefined`
109
+ if (!this.isAbsolute) {
110
+ return undefined;
111
+ }
112
+ const index = this.raw.indexOf(Path.separator, Path.separator.length);
113
+ if (index === -1) {
114
+ // '/foo/bar' -> '/'
115
+ return new Path(Path.separator);
116
+ }
117
+ // '/c:/foo/bar' -> '/c:'
118
+ // '/foo/bar' -> '/'
119
+ return new Path(this.raw.substr(0, index)).root;
120
+ }
121
+
122
+ get dir(): Path {
123
+ if (this._dir === undefined) {
124
+ this._dir = this.computeDir();
125
+ }
126
+ return this._dir;
127
+ }
128
+
129
+ protected computeDir(): Path {
130
+ if (this.isRoot) {
131
+ return this;
132
+ }
133
+ const lastIndex = this.raw.lastIndexOf(Path.separator);
134
+ if (lastIndex === -1) {
135
+ return this;
136
+ }
137
+ if (this.isAbsolute) {
138
+ const firstIndex = this.raw.indexOf(Path.separator);
139
+ if (firstIndex === lastIndex) {
140
+ return new Path(this.raw.substr(0, firstIndex + 1));
141
+ }
142
+ }
143
+ return new Path(this.raw.substr(0, lastIndex));
144
+ }
145
+
146
+ join(...paths: string[]): Path {
147
+ const code = this.raw.charCodeAt(0);
148
+ const isWindows = isWindowsDeviceRoot(code);
149
+
150
+ /**
151
+ * 只针对 IDE 后端运行在 Windows 的情况
152
+ * join('C:\\path\\to\\file', 'path/to/other') === 'C:\\path\\to\\file\\path\\to\\other'
153
+ */
154
+ if (isWindows) {
155
+ return new Path(win32.join(this.raw, ...paths));
156
+ }
157
+
158
+ const relativePath = paths.filter((s) => !!s).join(Path.separator);
159
+ if (!relativePath) {
160
+ return this;
161
+ }
162
+
163
+ if (this.raw.endsWith(Path.separator) || relativePath.startsWith(Path.separator)) {
164
+ return new Path(posix.join(this.raw, relativePath));
165
+ }
166
+ return new Path(posix.join(this.raw, Path.separator, relativePath));
167
+ }
168
+
169
+ toString(): string {
170
+ return this.raw;
171
+ }
172
+
173
+ relative(path: Path): Path | undefined {
174
+ if (this.raw === path.raw) {
175
+ return new Path('');
176
+ }
177
+ if (!this.raw || !path.raw) {
178
+ return undefined;
179
+ }
180
+ const raw = this.base ? this.raw + Path.separator : this.raw;
181
+ if (!path.raw.startsWith(raw)) {
182
+ return undefined;
183
+ }
184
+ const relativePath = path.raw.substr(raw.length);
185
+ return new Path(relativePath);
186
+ }
187
+
188
+ isEqualOrParent(path: Path): boolean {
189
+ return !!this.relative(path);
190
+ }
191
+
192
+ isEqual(path: Path): boolean {
193
+ return this.raw === path.raw;
194
+ }
195
+
196
+ relativity(path: Path): number {
197
+ const relative = this.relative(path);
198
+ if (relative) {
199
+ const relativeStr = relative.toString();
200
+ if (relativeStr === '') {
201
+ return 0;
202
+ }
203
+ return relativeStr.split(Path.separator).length;
204
+ }
205
+ return -1;
206
+ }
207
+ }
208
+
209
+ const CHAR_UPPERCASE_A = 65; /* A */
210
+ const CHAR_LOWERCASE_A = 97; /* a */
211
+ const CHAR_UPPERCASE_Z = 90; /* Z */
212
+ const CHAR_LOWERCASE_Z = 122; /* z */
213
+ const CHAR_DOT = 46; /* . */
214
+ const CHAR_FORWARD_SLASH = 47; /* / */
215
+ const CHAR_BACKWARD_SLASH = 92; /* \ */
216
+ const CHAR_COLON = 58; /* : */
217
+ const CHAR_QUESTION_MARK = 63; /* ? */
218
+
219
+ class ErrorInvalidArgType extends Error {
220
+ code: 'ERR_INVALID_ARG_TYPE';
221
+ constructor(name: string, expected: string, actual: any) {
222
+ // determiner: 'must be' or 'must not be'
223
+ let determiner;
224
+ if (typeof expected === 'string' && expected.indexOf('not ') === 0) {
225
+ determiner = 'must not be';
226
+ expected = expected.replace(/^not /, '');
227
+ } else {
228
+ determiner = 'must be';
229
+ }
230
+
231
+ const type = name.indexOf('.') !== -1 ? 'property' : 'argument';
232
+ let msg = `The "${name}" ${type} ${determiner} of type ${expected}`;
233
+
234
+ msg += `. Received type ${typeof actual}`;
235
+ super(msg);
236
+ }
237
+ }
238
+
239
+ function validateString(value: string, name: string) {
240
+ if (typeof value !== 'string') {
241
+ throw new ErrorInvalidArgType(name, 'string', value);
242
+ }
243
+ }
244
+
245
+ function isPathSeparator(code?: number | undefined) {
246
+ return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
247
+ }
248
+
249
+ function isPosixPathSeparator(code?: number | undefined): boolean {
250
+ return code === CHAR_FORWARD_SLASH;
251
+ }
252
+
253
+ function isWindowsDeviceRoot(code: number) {
254
+ return (
255
+ (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z)
256
+ );
257
+ }
258
+
259
+ // Resolves . and .. elements in a path with directory names
260
+ function normalizeString(
261
+ path: string,
262
+ allowAboveRoot: boolean,
263
+ separator: string,
264
+ isPathSeparator: (code?: number) => boolean,
265
+ ) {
266
+ let res = '';
267
+ let lastSegmentLength = 0;
268
+ let lastSlash = -1;
269
+ let dots = 0;
270
+ let code;
271
+ for (let i = 0; i <= path.length; ++i) {
272
+ if (i < path.length) {
273
+ code = path.charCodeAt(i);
274
+ } else if (isPathSeparator(code)) {
275
+ break;
276
+ } else {
277
+ code = CHAR_FORWARD_SLASH;
278
+ }
279
+
280
+ if (isPathSeparator(code)) {
281
+ if (lastSlash === i - 1 || dots === 1) {
282
+ // NOOP
283
+ } else if (lastSlash !== i - 1 && dots === 2) {
284
+ if (
285
+ res.length < 2 ||
286
+ lastSegmentLength !== 2 ||
287
+ res.charCodeAt(res.length - 1) !== CHAR_DOT ||
288
+ res.charCodeAt(res.length - 2) !== CHAR_DOT
289
+ ) {
290
+ if (res.length > 2) {
291
+ const lastSlashIndex = res.lastIndexOf(separator);
292
+ if (lastSlashIndex === -1) {
293
+ res = '';
294
+ lastSegmentLength = 0;
295
+ } else {
296
+ res = res.slice(0, lastSlashIndex);
297
+ lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
298
+ }
299
+ lastSlash = i;
300
+ dots = 0;
301
+ continue;
302
+ } else if (res.length === 2 || res.length === 1) {
303
+ res = '';
304
+ lastSegmentLength = 0;
305
+ lastSlash = i;
306
+ dots = 0;
307
+ continue;
308
+ }
309
+ }
310
+ if (allowAboveRoot) {
311
+ if (res.length > 0) {
312
+ res += `${separator}..`;
313
+ } else {
314
+ res = '..';
315
+ }
316
+ lastSegmentLength = 2;
317
+ }
318
+ } else {
319
+ if (res.length > 0) {
320
+ res += separator + path.slice(lastSlash + 1, i);
321
+ } else {
322
+ res = path.slice(lastSlash + 1, i);
323
+ }
324
+ lastSegmentLength = i - lastSlash - 1;
325
+ }
326
+ lastSlash = i;
327
+ dots = 0;
328
+ } else if (code === CHAR_DOT && dots !== -1) {
329
+ ++dots;
330
+ } else {
331
+ dots = -1;
332
+ }
333
+ }
334
+ return res;
335
+ }
336
+
337
+ function _format(sep: string, pathObject: ParsedPath) {
338
+ const dir = pathObject.dir || pathObject.root;
339
+ const base = pathObject.base || (pathObject.name || '') + (pathObject.ext || '');
340
+ if (!dir) {
341
+ return base;
342
+ }
343
+ if (dir === pathObject.root) {
344
+ return dir + base;
345
+ }
346
+ return dir + sep + base;
347
+ }
348
+
349
+ interface ParsedPath {
350
+ root: string;
351
+ dir: string;
352
+ base: string;
353
+ ext: string;
354
+ name: string;
355
+ }
356
+
357
+ export interface IPath {
358
+ normalize(path: string): string;
359
+ isAbsolute(path: string): boolean;
360
+ join(...paths: string[]): string;
361
+ resolve(...pathSegments: string[]): string;
362
+ relative(from: string, to: string): string;
363
+ dirname(path: string): string;
364
+ basename(path: string, ext?: string): string;
365
+ extname(path: string): string;
366
+ format(pathObject: ParsedPath): string;
367
+ parse(path: string): ParsedPath;
368
+ toNamespacedPath(path: string): string;
369
+ sep: '\\' | '/';
370
+ delimiter: string;
371
+ win32: IPath | null;
372
+ posix: IPath | null;
373
+ }
374
+
375
+ export const win32: IPath = {
376
+ // path.resolve([from ...], to)
377
+ resolve(...pathSegments: string[]): string {
378
+ let resolvedDevice = '';
379
+ let resolvedTail = '';
380
+ let resolvedAbsolute = false;
381
+
382
+ for (let i = pathSegments.length - 1; i >= -1; i--) {
383
+ let path;
384
+ if (i >= 0) {
385
+ path = pathSegments[i];
386
+ } else if (!resolvedDevice) {
387
+ path = process.cwd();
388
+ } else {
389
+ // Windows has the concept of drive-specific current working
390
+ // directories. If we've resolved a drive letter but not yet an
391
+ // absolute path, get cwd for that drive, or the process cwd if
392
+ // the drive cwd is not available. We're sure the device is not
393
+ // a UNC path at this points, because UNC paths are always absolute.
394
+ path = process.env['=' + resolvedDevice] || process.cwd();
395
+
396
+ // Verify that a cwd was found and that it actually points
397
+ // to our drive. If not, default to the drive's root.
398
+ if (path === undefined || path.slice(0, 3).toLowerCase() !== resolvedDevice.toLowerCase() + '\\') {
399
+ path = resolvedDevice + '\\';
400
+ }
401
+ }
402
+
403
+ validateString(path, 'path');
404
+
405
+ // Skip empty entries
406
+ if (path.length === 0) {
407
+ continue;
408
+ }
409
+
410
+ const len = path.length;
411
+ let rootEnd = 0;
412
+ let device = '';
413
+ let isAbsolute = false;
414
+ const code = path.charCodeAt(0);
415
+
416
+ // Try to match a root
417
+ if (len > 1) {
418
+ if (isPathSeparator(code)) {
419
+ // Possible UNC root
420
+
421
+ // If we started with a separator, we know we at least have an
422
+ // absolute path of some kind (UNC or otherwise)
423
+ isAbsolute = true;
424
+
425
+ if (isPathSeparator(path.charCodeAt(1))) {
426
+ // Matched double path separator at beginning
427
+ let j = 2;
428
+ let last = j;
429
+ // Match 1 or more non-path separators
430
+ for (; j < len; ++j) {
431
+ if (isPathSeparator(path.charCodeAt(j))) {
432
+ break;
433
+ }
434
+ }
435
+ if (j < len && j !== last) {
436
+ const firstPart = path.slice(last, j);
437
+ // Matched!
438
+ last = j;
439
+ // Match 1 or more path separators
440
+ for (; j < len; ++j) {
441
+ if (!isPathSeparator(path.charCodeAt(j))) {
442
+ break;
443
+ }
444
+ }
445
+ if (j < len && j !== last) {
446
+ // Matched!
447
+ last = j;
448
+ // Match 1 or more non-path separators
449
+ for (; j < len; ++j) {
450
+ if (isPathSeparator(path.charCodeAt(j))) {
451
+ break;
452
+ }
453
+ }
454
+ if (j === len) {
455
+ // We matched a UNC root only
456
+
457
+ device = '\\\\' + firstPart + '\\' + path.slice(last);
458
+ rootEnd = j;
459
+ } else if (j !== last) {
460
+ // We matched a UNC root with leftovers
461
+
462
+ device = '\\\\' + firstPart + '\\' + path.slice(last, j);
463
+ rootEnd = j;
464
+ }
465
+ }
466
+ }
467
+ } else {
468
+ rootEnd = 1;
469
+ }
470
+ } else if (isWindowsDeviceRoot(code)) {
471
+ // Possible device root
472
+
473
+ if (path.charCodeAt(1) === CHAR_COLON) {
474
+ device = path.slice(0, 2);
475
+ rootEnd = 2;
476
+ if (len > 2) {
477
+ if (isPathSeparator(path.charCodeAt(2))) {
478
+ // Treat separator following drive name as an absolute path
479
+ // indicator
480
+ isAbsolute = true;
481
+ rootEnd = 3;
482
+ }
483
+ }
484
+ }
485
+ }
486
+ } else if (isPathSeparator(code)) {
487
+ // `path` contains just a path separator
488
+ rootEnd = 1;
489
+ isAbsolute = true;
490
+ }
491
+
492
+ if (device.length > 0 && resolvedDevice.length > 0 && device.toLowerCase() !== resolvedDevice.toLowerCase()) {
493
+ // This path points to another device so it is not applicable
494
+ continue;
495
+ }
496
+
497
+ if (resolvedDevice.length === 0 && device.length > 0) {
498
+ resolvedDevice = device;
499
+ }
500
+ if (!resolvedAbsolute) {
501
+ resolvedTail = path.slice(rootEnd) + '\\' + resolvedTail;
502
+ resolvedAbsolute = isAbsolute;
503
+ }
504
+
505
+ if (resolvedDevice.length > 0 && resolvedAbsolute) {
506
+ break;
507
+ }
508
+ }
509
+
510
+ // At this point the path should be resolved to a full absolute path,
511
+ // but handle relative paths to be safe (might happen when process.cwd()
512
+ // fails)
513
+
514
+ // Normalize the tail path
515
+ resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, '\\', isPathSeparator);
516
+
517
+ return resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail || '.';
518
+ },
519
+
520
+ normalize(path: string): string {
521
+ validateString(path, 'path');
522
+ const len = path.length;
523
+ if (len === 0) {
524
+ return '.';
525
+ }
526
+ let rootEnd = 0;
527
+ let device;
528
+ let isAbsolute = false;
529
+ const code = path.charCodeAt(0);
530
+
531
+ // Try to match a root
532
+ if (len > 1) {
533
+ if (isPathSeparator(code)) {
534
+ // Possible UNC root
535
+
536
+ // If we started with a separator, we know we at least have an absolute
537
+ // path of some kind (UNC or otherwise)
538
+ isAbsolute = true;
539
+
540
+ if (isPathSeparator(path.charCodeAt(1))) {
541
+ // Matched double path separator at beginning
542
+ let j = 2;
543
+ let last = j;
544
+ // Match 1 or more non-path separators
545
+ for (; j < len; ++j) {
546
+ if (isPathSeparator(path.charCodeAt(j))) {
547
+ break;
548
+ }
549
+ }
550
+ if (j < len && j !== last) {
551
+ const firstPart = path.slice(last, j);
552
+ // Matched!
553
+ last = j;
554
+ // Match 1 or more path separators
555
+ for (; j < len; ++j) {
556
+ if (!isPathSeparator(path.charCodeAt(j))) {
557
+ break;
558
+ }
559
+ }
560
+ if (j < len && j !== last) {
561
+ // Matched!
562
+ last = j;
563
+ // Match 1 or more non-path separators
564
+ for (; j < len; ++j) {
565
+ if (isPathSeparator(path.charCodeAt(j))) {
566
+ break;
567
+ }
568
+ }
569
+ if (j === len) {
570
+ // We matched a UNC root only
571
+ // Return the normalized version of the UNC root since there
572
+ // is nothing left to process
573
+
574
+ return '\\\\' + firstPart + '\\' + path.slice(last) + '\\';
575
+ } else if (j !== last) {
576
+ // We matched a UNC root with leftovers
577
+
578
+ device = '\\\\' + firstPart + '\\' + path.slice(last, j);
579
+ rootEnd = j;
580
+ }
581
+ }
582
+ }
583
+ } else {
584
+ rootEnd = 1;
585
+ }
586
+ } else if (isWindowsDeviceRoot(code)) {
587
+ // Possible device root
588
+
589
+ if (path.charCodeAt(1) === CHAR_COLON) {
590
+ device = path.slice(0, 2);
591
+ rootEnd = 2;
592
+ if (len > 2) {
593
+ if (isPathSeparator(path.charCodeAt(2))) {
594
+ // Treat separator following drive name as an absolute path
595
+ // indicator
596
+ isAbsolute = true;
597
+ rootEnd = 3;
598
+ }
599
+ }
600
+ }
601
+ }
602
+ } else if (isPathSeparator(code)) {
603
+ // `path` contains just a path separator, exit early to avoid unnecessary
604
+ // work
605
+ return '\\';
606
+ }
607
+
608
+ let tail;
609
+ if (rootEnd < len) {
610
+ tail = normalizeString(path.slice(rootEnd), !isAbsolute, '\\', isPathSeparator);
611
+ } else {
612
+ tail = '';
613
+ }
614
+ if (tail.length === 0 && !isAbsolute) {
615
+ tail = '.';
616
+ }
617
+ if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) {
618
+ tail += '\\';
619
+ }
620
+ if (device === undefined) {
621
+ if (isAbsolute) {
622
+ if (tail.length > 0) {
623
+ return '\\' + tail;
624
+ } else {
625
+ return '\\';
626
+ }
627
+ } else if (tail.length > 0) {
628
+ return tail;
629
+ } else {
630
+ return '';
631
+ }
632
+ } else if (isAbsolute) {
633
+ if (tail.length > 0) {
634
+ return device + '\\' + tail;
635
+ } else {
636
+ return device + '\\';
637
+ }
638
+ } else if (tail.length > 0) {
639
+ return device + tail;
640
+ } else {
641
+ return device;
642
+ }
643
+ },
644
+
645
+ isAbsolute(path: string): boolean {
646
+ validateString(path, 'path');
647
+ const len = path.length;
648
+ if (len === 0) {
649
+ return false;
650
+ }
651
+
652
+ const code = path.charCodeAt(0);
653
+ if (isPathSeparator(code)) {
654
+ return true;
655
+ } else if (isWindowsDeviceRoot(code)) {
656
+ // Possible device root
657
+
658
+ if (len > 2 && path.charCodeAt(1) === CHAR_COLON) {
659
+ if (isPathSeparator(path.charCodeAt(2))) {
660
+ return true;
661
+ }
662
+ }
663
+ }
664
+ return false;
665
+ },
666
+
667
+ join(...paths: string[]): string {
668
+ if (paths.length === 0) {
669
+ return '.';
670
+ }
671
+
672
+ let joined;
673
+ let firstPart: string | undefined;
674
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
675
+ for (let i = 0; i < paths.length; ++i) {
676
+ const arg = paths[i];
677
+ validateString(arg, 'path');
678
+ if (arg.length > 0) {
679
+ if (joined === undefined) {
680
+ joined = firstPart = arg;
681
+ } else {
682
+ joined += '\\' + arg;
683
+ }
684
+ }
685
+ }
686
+
687
+ if (joined === undefined) {
688
+ return '.';
689
+ }
690
+
691
+ // Make sure that the joined path doesn't start with two slashes, because
692
+ // normalize() will mistake it for an UNC path then.
693
+ //
694
+ // This step is skipped when it is very clear that the user actually
695
+ // intended to point at an UNC path. This is assumed when the first
696
+ // non-empty string arguments starts with exactly two slashes followed by
697
+ // at least one more non-slash character.
698
+ //
699
+ // Note that for normalize() to treat a path as an UNC path it needs to
700
+ // have at least 2 components, so we don't filter for that here.
701
+ // This means that the user can use join to construct UNC paths from
702
+ // a server name and a share name; for example:
703
+ // path.join('//server', 'share') -> '\\\\server\\share\\')
704
+ let needsReplace = true;
705
+ let slashCount = 0;
706
+ if (typeof firstPart === 'string' && isPathSeparator(firstPart.charCodeAt(0))) {
707
+ ++slashCount;
708
+ const firstLen = firstPart.length;
709
+ if (firstLen > 1) {
710
+ if (isPathSeparator(firstPart.charCodeAt(1))) {
711
+ ++slashCount;
712
+ if (firstLen > 2) {
713
+ if (isPathSeparator(firstPart.charCodeAt(2))) {
714
+ ++slashCount;
715
+ } else {
716
+ // We matched a UNC path in the first part
717
+ needsReplace = false;
718
+ }
719
+ }
720
+ }
721
+ }
722
+ }
723
+ if (needsReplace) {
724
+ // Find any more consecutive slashes we need to replace
725
+ for (; slashCount < joined.length; ++slashCount) {
726
+ if (!isPathSeparator(joined.charCodeAt(slashCount))) {
727
+ break;
728
+ }
729
+ }
730
+
731
+ // Replace the slashes if needed
732
+ if (slashCount >= 2) {
733
+ joined = '\\' + joined.slice(slashCount);
734
+ }
735
+ }
736
+
737
+ return win32.normalize(joined);
738
+ },
739
+
740
+ // It will solve the relative path from `from` to `to`, for instance:
741
+ // from = 'C:\\orandea\\test\\aaa'
742
+ // to = 'C:\\orandea\\impl\\bbb'
743
+ // The output of the function should be: '..\\..\\impl\\bbb'
744
+ relative(from: string, to: string): string {
745
+ validateString(from, 'from');
746
+ validateString(to, 'to');
747
+
748
+ if (from === to) {
749
+ return '';
750
+ }
751
+
752
+ const fromOrig = win32.resolve(from);
753
+ const toOrig = win32.resolve(to);
754
+
755
+ if (fromOrig === toOrig) {
756
+ return '';
757
+ }
758
+
759
+ from = fromOrig.toLowerCase();
760
+ to = toOrig.toLowerCase();
761
+
762
+ if (from === to) {
763
+ return '';
764
+ }
765
+
766
+ // Trim any leading backslashes
767
+ let fromStart = 0;
768
+ for (; fromStart < from.length; ++fromStart) {
769
+ if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) {
770
+ break;
771
+ }
772
+ }
773
+ // Trim trailing backslashes (applicable to UNC paths only)
774
+ let fromEnd = from.length;
775
+ for (; fromEnd - 1 > fromStart; --fromEnd) {
776
+ if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) {
777
+ break;
778
+ }
779
+ }
780
+ const fromLen = fromEnd - fromStart;
781
+
782
+ // Trim any leading backslashes
783
+ let toStart = 0;
784
+ for (; toStart < to.length; ++toStart) {
785
+ if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) {
786
+ break;
787
+ }
788
+ }
789
+ // Trim trailing backslashes (applicable to UNC paths only)
790
+ let toEnd = to.length;
791
+ for (; toEnd - 1 > toStart; --toEnd) {
792
+ if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) {
793
+ break;
794
+ }
795
+ }
796
+ const toLen = toEnd - toStart;
797
+
798
+ // Compare paths to find the longest common path from root
799
+ const length = fromLen < toLen ? fromLen : toLen;
800
+ let lastCommonSep = -1;
801
+ let i = 0;
802
+ for (; i <= length; ++i) {
803
+ if (i === length) {
804
+ if (toLen > length) {
805
+ if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) {
806
+ // We get here if `from` is the exact base path for `to`.
807
+ // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz'
808
+ return toOrig.slice(toStart + i + 1);
809
+ } else if (i === 2) {
810
+ // We get here if `from` is the device root.
811
+ // For example: from='C:\\'; to='C:\\foo'
812
+ return toOrig.slice(toStart + i);
813
+ }
814
+ }
815
+ if (fromLen > length) {
816
+ if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) {
817
+ // We get here if `to` is the exact base path for `from`.
818
+ // For example: from='C:\\foo\\bar'; to='C:\\foo'
819
+ lastCommonSep = i;
820
+ } else if (i === 2) {
821
+ // We get here if `to` is the device root.
822
+ // For example: from='C:\\foo\\bar'; to='C:\\'
823
+ lastCommonSep = 3;
824
+ }
825
+ }
826
+ break;
827
+ }
828
+ const fromCode = from.charCodeAt(fromStart + i);
829
+ const toCode = to.charCodeAt(toStart + i);
830
+ if (fromCode !== toCode) {
831
+ break;
832
+ } else if (fromCode === CHAR_BACKWARD_SLASH) {
833
+ lastCommonSep = i;
834
+ }
835
+ }
836
+
837
+ // We found a mismatch before the first common path separator was seen, so
838
+ // return the original `to`.
839
+ if (i !== length && lastCommonSep === -1) {
840
+ return toOrig;
841
+ }
842
+
843
+ let out = '';
844
+ if (lastCommonSep === -1) {
845
+ lastCommonSep = 0;
846
+ }
847
+ // Generate the relative path based on the path difference between `to` and
848
+ // `from`
849
+ for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
850
+ if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) {
851
+ if (out.length === 0) {
852
+ out += '..';
853
+ } else {
854
+ out += '\\..';
855
+ }
856
+ }
857
+ }
858
+
859
+ // Lastly, append the rest of the destination (`to`) path that comes after
860
+ // the common path parts
861
+ if (out.length > 0) {
862
+ return out + toOrig.slice(toStart + lastCommonSep, toEnd);
863
+ } else {
864
+ toStart += lastCommonSep;
865
+ if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) {
866
+ ++toStart;
867
+ }
868
+ return toOrig.slice(toStart, toEnd);
869
+ }
870
+ },
871
+
872
+ toNamespacedPath(path: string): string {
873
+ // Note: this will *probably* throw somewhere.
874
+ if (typeof path !== 'string') {
875
+ return path;
876
+ }
877
+
878
+ if (path.length === 0) {
879
+ return '';
880
+ }
881
+
882
+ const resolvedPath = win32.resolve(path);
883
+
884
+ if (resolvedPath.length >= 3) {
885
+ if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) {
886
+ // Possible UNC root
887
+
888
+ if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) {
889
+ const code = resolvedPath.charCodeAt(2);
890
+ if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) {
891
+ // Matched non-long UNC root, convert the path to a long UNC path
892
+ return '\\\\?\\UNC\\' + resolvedPath.slice(2);
893
+ }
894
+ }
895
+ } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) {
896
+ // Possible device root
897
+
898
+ if (resolvedPath.charCodeAt(1) === CHAR_COLON && resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH) {
899
+ // Matched device root, convert the path to a long UNC path
900
+ return '\\\\?\\' + resolvedPath;
901
+ }
902
+ }
903
+ }
904
+
905
+ return path;
906
+ },
907
+
908
+ dirname(path: string): string {
909
+ validateString(path, 'path');
910
+ const len = path.length;
911
+ if (len === 0) {
912
+ return '.';
913
+ }
914
+ let rootEnd = -1;
915
+ let end = -1;
916
+ let matchedSlash = true;
917
+ let offset = 0;
918
+ const code = path.charCodeAt(0);
919
+
920
+ // Try to match a root
921
+ if (len > 1) {
922
+ if (isPathSeparator(code)) {
923
+ // Possible UNC root
924
+
925
+ rootEnd = offset = 1;
926
+
927
+ if (isPathSeparator(path.charCodeAt(1))) {
928
+ // Matched double path separator at beginning
929
+ let j = 2;
930
+ let last = j;
931
+ // Match 1 or more non-path separators
932
+ for (; j < len; ++j) {
933
+ if (isPathSeparator(path.charCodeAt(j))) {
934
+ break;
935
+ }
936
+ }
937
+ if (j < len && j !== last) {
938
+ // Matched!
939
+ last = j;
940
+ // Match 1 or more path separators
941
+ for (; j < len; ++j) {
942
+ if (!isPathSeparator(path.charCodeAt(j))) {
943
+ break;
944
+ }
945
+ }
946
+ if (j < len && j !== last) {
947
+ // Matched!
948
+ last = j;
949
+ // Match 1 or more non-path separators
950
+ for (; j < len; ++j) {
951
+ if (isPathSeparator(path.charCodeAt(j))) {
952
+ break;
953
+ }
954
+ }
955
+ if (j === len) {
956
+ // We matched a UNC root only
957
+ return path;
958
+ }
959
+ if (j !== last) {
960
+ // We matched a UNC root with leftovers
961
+
962
+ // Offset by 1 to include the separator after the UNC root to
963
+ // treat it as a "normal root" on top of a (UNC) root
964
+ rootEnd = offset = j + 1;
965
+ }
966
+ }
967
+ }
968
+ }
969
+ } else if (isWindowsDeviceRoot(code)) {
970
+ // Possible device root
971
+
972
+ if (path.charCodeAt(1) === CHAR_COLON) {
973
+ rootEnd = offset = 2;
974
+ if (len > 2) {
975
+ if (isPathSeparator(path.charCodeAt(2))) {
976
+ rootEnd = offset = 3;
977
+ }
978
+ }
979
+ }
980
+ }
981
+ } else if (isPathSeparator(code)) {
982
+ // `path` contains just a path separator, exit early to avoid
983
+ // unnecessary work
984
+ return path;
985
+ }
986
+
987
+ for (let i = len - 1; i >= offset; --i) {
988
+ if (isPathSeparator(path.charCodeAt(i))) {
989
+ if (!matchedSlash) {
990
+ end = i;
991
+ break;
992
+ }
993
+ } else {
994
+ // We saw the first non-path separator
995
+ matchedSlash = false;
996
+ }
997
+ }
998
+
999
+ if (end === -1) {
1000
+ if (rootEnd === -1) {
1001
+ return '.';
1002
+ } else {
1003
+ end = rootEnd;
1004
+ }
1005
+ }
1006
+ return path.slice(0, end);
1007
+ },
1008
+
1009
+ basename(path: string, ext?: string): string {
1010
+ if (ext !== undefined) {
1011
+ validateString(ext, 'ext');
1012
+ }
1013
+ validateString(path, 'path');
1014
+ let start = 0;
1015
+ let end = -1;
1016
+ let matchedSlash = true;
1017
+ let i;
1018
+
1019
+ // Check for a drive letter prefix so as not to mistake the following
1020
+ // path separator as an extra separator at the end of the path that can be
1021
+ // disregarded
1022
+ if (path.length >= 2) {
1023
+ const drive = path.charCodeAt(0);
1024
+ if (isWindowsDeviceRoot(drive)) {
1025
+ if (path.charCodeAt(1) === CHAR_COLON) {
1026
+ start = 2;
1027
+ }
1028
+ }
1029
+ }
1030
+
1031
+ if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
1032
+ if (ext.length === path.length && ext === path) {
1033
+ return '';
1034
+ }
1035
+ let extIdx = ext.length - 1;
1036
+ let firstNonSlashEnd = -1;
1037
+ for (i = path.length - 1; i >= start; --i) {
1038
+ const code = path.charCodeAt(i);
1039
+ if (isPathSeparator(code)) {
1040
+ // If we reached a path separator that was not part of a set of path
1041
+ // separators at the end of the string, stop now
1042
+ if (!matchedSlash) {
1043
+ start = i + 1;
1044
+ break;
1045
+ }
1046
+ } else {
1047
+ if (firstNonSlashEnd === -1) {
1048
+ // We saw the first non-path separator, remember this index in case
1049
+ // we need it if the extension ends up not matching
1050
+ matchedSlash = false;
1051
+ firstNonSlashEnd = i + 1;
1052
+ }
1053
+ if (extIdx >= 0) {
1054
+ // Try to match the explicit extension
1055
+ if (code === ext.charCodeAt(extIdx)) {
1056
+ if (--extIdx === -1) {
1057
+ // We matched the extension, so mark this as the end of our path
1058
+ // component
1059
+ end = i;
1060
+ }
1061
+ } else {
1062
+ // Extension does not match, so our result is the entire path
1063
+ // component
1064
+ extIdx = -1;
1065
+ end = firstNonSlashEnd;
1066
+ }
1067
+ }
1068
+ }
1069
+ }
1070
+
1071
+ if (start === end) {
1072
+ end = firstNonSlashEnd;
1073
+ } else if (end === -1) {
1074
+ end = path.length;
1075
+ }
1076
+ return path.slice(start, end);
1077
+ } else {
1078
+ for (i = path.length - 1; i >= start; --i) {
1079
+ if (isPathSeparator(path.charCodeAt(i))) {
1080
+ // If we reached a path separator that was not part of a set of path
1081
+ // separators at the end of the string, stop now
1082
+ if (!matchedSlash) {
1083
+ start = i + 1;
1084
+ break;
1085
+ }
1086
+ } else if (end === -1) {
1087
+ // We saw the first non-path separator, mark this as the end of our
1088
+ // path component
1089
+ matchedSlash = false;
1090
+ end = i + 1;
1091
+ }
1092
+ }
1093
+
1094
+ if (end === -1) {
1095
+ return '';
1096
+ }
1097
+ return path.slice(start, end);
1098
+ }
1099
+ },
1100
+
1101
+ extname(path: string): string {
1102
+ validateString(path, 'path');
1103
+ let start = 0;
1104
+ let startDot = -1;
1105
+ let startPart = 0;
1106
+ let end = -1;
1107
+ let matchedSlash = true;
1108
+ // Track the state of characters (if any) we see before our first dot and
1109
+ // after any path separator we find
1110
+ let preDotState = 0;
1111
+
1112
+ // Check for a drive letter prefix so as not to mistake the following
1113
+ // path separator as an extra separator at the end of the path that can be
1114
+ // disregarded
1115
+
1116
+ if (path.length >= 2 && path.charCodeAt(1) === CHAR_COLON && isWindowsDeviceRoot(path.charCodeAt(0))) {
1117
+ start = startPart = 2;
1118
+ }
1119
+
1120
+ for (let i = path.length - 1; i >= start; --i) {
1121
+ const code = path.charCodeAt(i);
1122
+ if (isPathSeparator(code)) {
1123
+ // If we reached a path separator that was not part of a set of path
1124
+ // separators at the end of the string, stop now
1125
+ if (!matchedSlash) {
1126
+ startPart = i + 1;
1127
+ break;
1128
+ }
1129
+ continue;
1130
+ }
1131
+ if (end === -1) {
1132
+ // We saw the first non-path separator, mark this as the end of our
1133
+ // extension
1134
+ matchedSlash = false;
1135
+ end = i + 1;
1136
+ }
1137
+ if (code === CHAR_DOT) {
1138
+ // If this is our first dot, mark it as the start of our extension
1139
+ if (startDot === -1) {
1140
+ startDot = i;
1141
+ } else if (preDotState !== 1) {
1142
+ preDotState = 1;
1143
+ }
1144
+ } else if (startDot !== -1) {
1145
+ // We saw a non-dot and non-path separator before our dot, so we should
1146
+ // have a good chance at having a non-empty extension
1147
+ preDotState = -1;
1148
+ }
1149
+ }
1150
+
1151
+ if (
1152
+ startDot === -1 ||
1153
+ end === -1 ||
1154
+ // We saw a non-dot character immediately before the dot
1155
+ preDotState === 0 ||
1156
+ // The (right-most) trimmed path component is exactly '..'
1157
+ (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
1158
+ ) {
1159
+ return '';
1160
+ }
1161
+ return path.slice(startDot, end);
1162
+ },
1163
+
1164
+ format(pathObject): string {
1165
+ if (pathObject === null || typeof pathObject !== 'object') {
1166
+ throw new ErrorInvalidArgType('pathObject', 'Object', pathObject);
1167
+ }
1168
+
1169
+ return _format('\\', pathObject);
1170
+ },
1171
+
1172
+ parse(path) {
1173
+ validateString(path, 'path');
1174
+
1175
+ const ret = { root: '', dir: '', base: '', ext: '', name: '' };
1176
+ if (path.length === 0) {
1177
+ return ret;
1178
+ }
1179
+
1180
+ const len = path.length;
1181
+ let rootEnd = 0;
1182
+ let code = path.charCodeAt(0);
1183
+
1184
+ // Try to match a root
1185
+ if (len > 1) {
1186
+ if (isPathSeparator(code)) {
1187
+ // Possible UNC root
1188
+
1189
+ rootEnd = 1;
1190
+ if (isPathSeparator(path.charCodeAt(1))) {
1191
+ // Matched double path separator at beginning
1192
+ let j = 2;
1193
+ let last = j;
1194
+ // Match 1 or more non-path separators
1195
+ for (; j < len; ++j) {
1196
+ if (isPathSeparator(path.charCodeAt(j))) {
1197
+ break;
1198
+ }
1199
+ }
1200
+ if (j < len && j !== last) {
1201
+ // Matched!
1202
+ last = j;
1203
+ // Match 1 or more path separators
1204
+ for (; j < len; ++j) {
1205
+ if (!isPathSeparator(path.charCodeAt(j))) {
1206
+ break;
1207
+ }
1208
+ }
1209
+ if (j < len && j !== last) {
1210
+ // Matched!
1211
+ last = j;
1212
+ // Match 1 or more non-path separators
1213
+ for (; j < len; ++j) {
1214
+ if (isPathSeparator(path.charCodeAt(j))) {
1215
+ break;
1216
+ }
1217
+ }
1218
+ if (j === len) {
1219
+ // We matched a UNC root only
1220
+
1221
+ rootEnd = j;
1222
+ } else if (j !== last) {
1223
+ // We matched a UNC root with leftovers
1224
+
1225
+ rootEnd = j + 1;
1226
+ }
1227
+ }
1228
+ }
1229
+ }
1230
+ } else if (isWindowsDeviceRoot(code)) {
1231
+ // Possible device root
1232
+
1233
+ if (path.charCodeAt(1) === CHAR_COLON) {
1234
+ rootEnd = 2;
1235
+ if (len > 2) {
1236
+ if (isPathSeparator(path.charCodeAt(2))) {
1237
+ if (len === 3) {
1238
+ // `path` contains just a drive root, exit early to avoid
1239
+ // unnecessary work
1240
+ ret.root = ret.dir = path;
1241
+ return ret;
1242
+ }
1243
+ rootEnd = 3;
1244
+ }
1245
+ } else {
1246
+ // `path` contains just a drive root, exit early to avoid
1247
+ // unnecessary work
1248
+ ret.root = ret.dir = path;
1249
+ return ret;
1250
+ }
1251
+ }
1252
+ }
1253
+ } else if (isPathSeparator(code)) {
1254
+ // `path` contains just a path separator, exit early to avoid
1255
+ // unnecessary work
1256
+ ret.root = ret.dir = path;
1257
+ return ret;
1258
+ }
1259
+
1260
+ if (rootEnd > 0) {
1261
+ ret.root = path.slice(0, rootEnd);
1262
+ }
1263
+
1264
+ let startDot = -1;
1265
+ let startPart = rootEnd;
1266
+ let end = -1;
1267
+ let matchedSlash = true;
1268
+ let i = path.length - 1;
1269
+
1270
+ // Track the state of characters (if any) we see before our first dot and
1271
+ // after any path separator we find
1272
+ let preDotState = 0;
1273
+
1274
+ // Get non-dir info
1275
+ for (; i >= rootEnd; --i) {
1276
+ code = path.charCodeAt(i);
1277
+ if (isPathSeparator(code)) {
1278
+ // If we reached a path separator that was not part of a set of path
1279
+ // separators at the end of the string, stop now
1280
+ if (!matchedSlash) {
1281
+ startPart = i + 1;
1282
+ break;
1283
+ }
1284
+ continue;
1285
+ }
1286
+ if (end === -1) {
1287
+ // We saw the first non-path separator, mark this as the end of our
1288
+ // extension
1289
+ matchedSlash = false;
1290
+ end = i + 1;
1291
+ }
1292
+ if (code === CHAR_DOT) {
1293
+ // If this is our first dot, mark it as the start of our extension
1294
+ if (startDot === -1) {
1295
+ startDot = i;
1296
+ } else if (preDotState !== 1) {
1297
+ preDotState = 1;
1298
+ }
1299
+ } else if (startDot !== -1) {
1300
+ // We saw a non-dot and non-path separator before our dot, so we should
1301
+ // have a good chance at having a non-empty extension
1302
+ preDotState = -1;
1303
+ }
1304
+ }
1305
+
1306
+ if (
1307
+ startDot === -1 ||
1308
+ end === -1 ||
1309
+ // We saw a non-dot character immediately before the dot
1310
+ preDotState === 0 ||
1311
+ // The (right-most) trimmed path component is exactly '..'
1312
+ (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
1313
+ ) {
1314
+ if (end !== -1) {
1315
+ ret.base = ret.name = path.slice(startPart, end);
1316
+ }
1317
+ } else {
1318
+ ret.name = path.slice(startPart, startDot);
1319
+ ret.base = path.slice(startPart, end);
1320
+ ret.ext = path.slice(startDot, end);
1321
+ }
1322
+
1323
+ // If the directory is the root, use the entire root as the `dir` including
1324
+ // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the
1325
+ // trailing slash (`C:\abc\def` -> `C:\abc`).
1326
+ if (startPart > 0 && startPart !== rootEnd) {
1327
+ ret.dir = path.slice(0, startPart - 1);
1328
+ } else {
1329
+ ret.dir = ret.root;
1330
+ }
1331
+
1332
+ return ret;
1333
+ },
1334
+
1335
+ sep: '\\',
1336
+ delimiter: ';',
1337
+ win32: null,
1338
+ posix: null,
1339
+ };
1340
+
1341
+ export const posix: IPath = {
1342
+ // path.resolve([from ...], to)
1343
+ resolve(...pathSegments: string[]): string {
1344
+ let resolvedPath = '';
1345
+ let resolvedAbsolute = false;
1346
+
1347
+ for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
1348
+ let path;
1349
+ if (i >= 0) {
1350
+ path = pathSegments[i];
1351
+ } else {
1352
+ path = process.cwd();
1353
+ }
1354
+
1355
+ validateString(path, 'path');
1356
+
1357
+ // Skip empty entries
1358
+ if (path.length === 0) {
1359
+ continue;
1360
+ }
1361
+
1362
+ resolvedPath = path + '/' + resolvedPath;
1363
+ resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1364
+ }
1365
+
1366
+ // At this point the path should be resolved to a full absolute path, but
1367
+ // handle relative paths to be safe (might happen when process.cwd() fails)
1368
+
1369
+ // Normalize the path
1370
+ resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator);
1371
+
1372
+ if (resolvedAbsolute) {
1373
+ if (resolvedPath.length > 0) {
1374
+ return '/' + resolvedPath;
1375
+ } else {
1376
+ return '/';
1377
+ }
1378
+ } else if (resolvedPath.length > 0) {
1379
+ return resolvedPath;
1380
+ } else {
1381
+ return '.';
1382
+ }
1383
+ },
1384
+
1385
+ normalize(path: string): string {
1386
+ validateString(path, 'path');
1387
+
1388
+ if (path.length === 0) {
1389
+ return '.';
1390
+ }
1391
+
1392
+ const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1393
+ const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH;
1394
+
1395
+ // Normalize the path
1396
+ path = normalizeString(path, !isAbsolute, '/', isPosixPathSeparator);
1397
+
1398
+ if (path.length === 0 && !isAbsolute) {
1399
+ path = '.';
1400
+ }
1401
+ if (path.length > 0 && trailingSeparator) {
1402
+ path += '/';
1403
+ }
1404
+
1405
+ if (isAbsolute) {
1406
+ return '/' + path;
1407
+ }
1408
+ return path;
1409
+ },
1410
+
1411
+ isAbsolute(path: string): boolean {
1412
+ validateString(path, 'path');
1413
+ return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1414
+ },
1415
+
1416
+ join(...paths: string[]): string {
1417
+ if (paths.length === 0) {
1418
+ return '.';
1419
+ }
1420
+ let joined;
1421
+ for (let i = 0; i < paths.length; ++i) {
1422
+ const arg = arguments[i];
1423
+ validateString(arg, 'path');
1424
+ if (arg.length > 0) {
1425
+ if (joined === undefined) {
1426
+ joined = arg;
1427
+ } else {
1428
+ joined += '/' + arg;
1429
+ }
1430
+ }
1431
+ }
1432
+ if (joined === undefined) {
1433
+ return '.';
1434
+ }
1435
+ return posix.normalize(joined);
1436
+ },
1437
+
1438
+ relative(from: string, to: string): string {
1439
+ validateString(from, 'from');
1440
+ validateString(to, 'to');
1441
+
1442
+ if (from === to) {
1443
+ return '';
1444
+ }
1445
+
1446
+ from = posix.resolve(from);
1447
+ to = posix.resolve(to);
1448
+
1449
+ if (from === to) {
1450
+ return '';
1451
+ }
1452
+
1453
+ // Trim any leading backslashes
1454
+ let fromStart = 1;
1455
+ for (; fromStart < from.length; ++fromStart) {
1456
+ if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) {
1457
+ break;
1458
+ }
1459
+ }
1460
+ const fromEnd = from.length;
1461
+ const fromLen = fromEnd - fromStart;
1462
+
1463
+ // Trim any leading backslashes
1464
+ let toStart = 1;
1465
+ for (; toStart < to.length; ++toStart) {
1466
+ if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) {
1467
+ break;
1468
+ }
1469
+ }
1470
+ const toEnd = to.length;
1471
+ const toLen = toEnd - toStart;
1472
+
1473
+ // Compare paths to find the longest common path from root
1474
+ const length = fromLen < toLen ? fromLen : toLen;
1475
+ let lastCommonSep = -1;
1476
+ let i = 0;
1477
+ for (; i <= length; ++i) {
1478
+ if (i === length) {
1479
+ if (toLen > length) {
1480
+ if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) {
1481
+ // We get here if `from` is the exact base path for `to`.
1482
+ // For example: from='/foo/bar'; to='/foo/bar/baz'
1483
+ return to.slice(toStart + i + 1);
1484
+ } else if (i === 0) {
1485
+ // We get here if `from` is the root
1486
+ // For example: from='/'; to='/foo'
1487
+ return to.slice(toStart + i);
1488
+ }
1489
+ } else if (fromLen > length) {
1490
+ if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) {
1491
+ // We get here if `to` is the exact base path for `from`.
1492
+ // For example: from='/foo/bar/baz'; to='/foo/bar'
1493
+ lastCommonSep = i;
1494
+ } else if (i === 0) {
1495
+ // We get here if `to` is the root.
1496
+ // For example: from='/foo'; to='/'
1497
+ lastCommonSep = 0;
1498
+ }
1499
+ }
1500
+ break;
1501
+ }
1502
+ const fromCode = from.charCodeAt(fromStart + i);
1503
+ const toCode = to.charCodeAt(toStart + i);
1504
+ if (fromCode !== toCode) {
1505
+ break;
1506
+ } else if (fromCode === CHAR_FORWARD_SLASH) {
1507
+ lastCommonSep = i;
1508
+ }
1509
+ }
1510
+
1511
+ let out = '';
1512
+ // Generate the relative path based on the path difference between `to`
1513
+ // and `from`
1514
+ for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
1515
+ if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) {
1516
+ if (out.length === 0) {
1517
+ out += '..';
1518
+ } else {
1519
+ out += '/..';
1520
+ }
1521
+ }
1522
+ }
1523
+
1524
+ // Lastly, append the rest of the destination (`to`) path that comes after
1525
+ // the common path parts
1526
+ if (out.length > 0) {
1527
+ return out + to.slice(toStart + lastCommonSep);
1528
+ } else {
1529
+ toStart += lastCommonSep;
1530
+ if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) {
1531
+ ++toStart;
1532
+ }
1533
+ return to.slice(toStart);
1534
+ }
1535
+ },
1536
+
1537
+ toNamespacedPath(path: string): string {
1538
+ // Non-op on posix systems
1539
+ return path;
1540
+ },
1541
+
1542
+ dirname(path: string): string {
1543
+ validateString(path, 'path');
1544
+ if (path.length === 0) {
1545
+ return '.';
1546
+ }
1547
+ const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1548
+ let end = -1;
1549
+ let matchedSlash = true;
1550
+ for (let i = path.length - 1; i >= 1; --i) {
1551
+ if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
1552
+ if (!matchedSlash) {
1553
+ end = i;
1554
+ break;
1555
+ }
1556
+ } else {
1557
+ // We saw the first non-path separator
1558
+ matchedSlash = false;
1559
+ }
1560
+ }
1561
+
1562
+ if (end === -1) {
1563
+ return hasRoot ? '/' : '.';
1564
+ }
1565
+ if (hasRoot && end === 1) {
1566
+ return '//';
1567
+ }
1568
+ return path.slice(0, end);
1569
+ },
1570
+
1571
+ basename(path: string, ext?: string): string {
1572
+ if (ext !== undefined) {
1573
+ validateString(ext, 'ext');
1574
+ }
1575
+ validateString(path, 'path');
1576
+
1577
+ let start = 0;
1578
+ let end = -1;
1579
+ let matchedSlash = true;
1580
+ let i;
1581
+
1582
+ if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
1583
+ if (ext.length === path.length && ext === path) {
1584
+ return '';
1585
+ }
1586
+ let extIdx = ext.length - 1;
1587
+ let firstNonSlashEnd = -1;
1588
+ for (i = path.length - 1; i >= 0; --i) {
1589
+ const code = path.charCodeAt(i);
1590
+ if (code === CHAR_FORWARD_SLASH) {
1591
+ // If we reached a path separator that was not part of a set of path
1592
+ // separators at the end of the string, stop now
1593
+ if (!matchedSlash) {
1594
+ start = i + 1;
1595
+ break;
1596
+ }
1597
+ } else {
1598
+ if (firstNonSlashEnd === -1) {
1599
+ // We saw the first non-path separator, remember this index in case
1600
+ // we need it if the extension ends up not matching
1601
+ matchedSlash = false;
1602
+ firstNonSlashEnd = i + 1;
1603
+ }
1604
+ if (extIdx >= 0) {
1605
+ // Try to match the explicit extension
1606
+ if (code === ext.charCodeAt(extIdx)) {
1607
+ if (--extIdx === -1) {
1608
+ // We matched the extension, so mark this as the end of our path
1609
+ // component
1610
+ end = i;
1611
+ }
1612
+ } else {
1613
+ // Extension does not match, so our result is the entire path
1614
+ // component
1615
+ extIdx = -1;
1616
+ end = firstNonSlashEnd;
1617
+ }
1618
+ }
1619
+ }
1620
+ }
1621
+
1622
+ if (start === end) {
1623
+ end = firstNonSlashEnd;
1624
+ } else if (end === -1) {
1625
+ end = path.length;
1626
+ }
1627
+ return path.slice(start, end);
1628
+ } else {
1629
+ for (i = path.length - 1; i >= 0; --i) {
1630
+ if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
1631
+ // If we reached a path separator that was not part of a set of path
1632
+ // separators at the end of the string, stop now
1633
+ if (!matchedSlash) {
1634
+ start = i + 1;
1635
+ break;
1636
+ }
1637
+ } else if (end === -1) {
1638
+ // We saw the first non-path separator, mark this as the end of our
1639
+ // path component
1640
+ matchedSlash = false;
1641
+ end = i + 1;
1642
+ }
1643
+ }
1644
+
1645
+ if (end === -1) {
1646
+ return '';
1647
+ }
1648
+ return path.slice(start, end);
1649
+ }
1650
+ },
1651
+
1652
+ extname(path: string): string {
1653
+ validateString(path, 'path');
1654
+ let startDot = -1;
1655
+ let startPart = 0;
1656
+ let end = -1;
1657
+ let matchedSlash = true;
1658
+ // Track the state of characters (if any) we see before our first dot and
1659
+ // after any path separator we find
1660
+ let preDotState = 0;
1661
+ for (let i = path.length - 1; i >= 0; --i) {
1662
+ const code = path.charCodeAt(i);
1663
+ if (code === CHAR_FORWARD_SLASH) {
1664
+ // If we reached a path separator that was not part of a set of path
1665
+ // separators at the end of the string, stop now
1666
+ if (!matchedSlash) {
1667
+ startPart = i + 1;
1668
+ break;
1669
+ }
1670
+ continue;
1671
+ }
1672
+ if (end === -1) {
1673
+ // We saw the first non-path separator, mark this as the end of our
1674
+ // extension
1675
+ matchedSlash = false;
1676
+ end = i + 1;
1677
+ }
1678
+ if (code === CHAR_DOT) {
1679
+ // If this is our first dot, mark it as the start of our extension
1680
+ if (startDot === -1) {
1681
+ startDot = i;
1682
+ } else if (preDotState !== 1) {
1683
+ preDotState = 1;
1684
+ }
1685
+ } else if (startDot !== -1) {
1686
+ // We saw a non-dot and non-path separator before our dot, so we should
1687
+ // have a good chance at having a non-empty extension
1688
+ preDotState = -1;
1689
+ }
1690
+ }
1691
+
1692
+ if (
1693
+ startDot === -1 ||
1694
+ end === -1 ||
1695
+ // We saw a non-dot character immediately before the dot
1696
+ preDotState === 0 ||
1697
+ // The (right-most) trimmed path component is exactly '..'
1698
+ (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
1699
+ ) {
1700
+ return '';
1701
+ }
1702
+ return path.slice(startDot, end);
1703
+ },
1704
+
1705
+ format(pathObject): string {
1706
+ if (pathObject === null || typeof pathObject !== 'object') {
1707
+ throw new ErrorInvalidArgType('pathObject', 'Object', pathObject);
1708
+ }
1709
+
1710
+ return _format('/', pathObject);
1711
+ },
1712
+
1713
+ parse(path: string): ParsedPath {
1714
+ validateString(path, 'path');
1715
+
1716
+ const ret = { root: '', dir: '', base: '', ext: '', name: '' };
1717
+ if (path.length === 0) {
1718
+ return ret;
1719
+ }
1720
+ const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
1721
+ let start;
1722
+ if (isAbsolute) {
1723
+ ret.root = '/';
1724
+ start = 1;
1725
+ } else {
1726
+ start = 0;
1727
+ }
1728
+ let startDot = -1;
1729
+ let startPart = 0;
1730
+ let end = -1;
1731
+ let matchedSlash = true;
1732
+ let i = path.length - 1;
1733
+
1734
+ // Track the state of characters (if any) we see before our first dot and
1735
+ // after any path separator we find
1736
+ let preDotState = 0;
1737
+
1738
+ // Get non-dir info
1739
+ for (; i >= start; --i) {
1740
+ const code = path.charCodeAt(i);
1741
+ if (code === CHAR_FORWARD_SLASH) {
1742
+ // If we reached a path separator that was not part of a set of path
1743
+ // separators at the end of the string, stop now
1744
+ if (!matchedSlash) {
1745
+ startPart = i + 1;
1746
+ break;
1747
+ }
1748
+ continue;
1749
+ }
1750
+ if (end === -1) {
1751
+ // We saw the first non-path separator, mark this as the end of our
1752
+ // extension
1753
+ matchedSlash = false;
1754
+ end = i + 1;
1755
+ }
1756
+ if (code === CHAR_DOT) {
1757
+ // If this is our first dot, mark it as the start of our extension
1758
+ if (startDot === -1) {
1759
+ startDot = i;
1760
+ } else if (preDotState !== 1) {
1761
+ preDotState = 1;
1762
+ }
1763
+ } else if (startDot !== -1) {
1764
+ // We saw a non-dot and non-path separator before our dot, so we should
1765
+ // have a good chance at having a non-empty extension
1766
+ preDotState = -1;
1767
+ }
1768
+ }
1769
+
1770
+ if (
1771
+ startDot === -1 ||
1772
+ end === -1 ||
1773
+ // We saw a non-dot character immediately before the dot
1774
+ preDotState === 0 ||
1775
+ // The (right-most) trimmed path component is exactly '..'
1776
+ (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
1777
+ ) {
1778
+ if (end !== -1) {
1779
+ if (startPart === 0 && isAbsolute) {
1780
+ ret.base = ret.name = path.slice(1, end);
1781
+ } else {
1782
+ ret.base = ret.name = path.slice(startPart, end);
1783
+ }
1784
+ }
1785
+ } else {
1786
+ if (startPart === 0 && isAbsolute) {
1787
+ ret.name = path.slice(1, startDot);
1788
+ ret.base = path.slice(1, end);
1789
+ } else {
1790
+ ret.name = path.slice(startPart, startDot);
1791
+ ret.base = path.slice(startPart, end);
1792
+ }
1793
+ ret.ext = path.slice(startDot, end);
1794
+ }
1795
+
1796
+ if (startPart > 0) {
1797
+ ret.dir = path.slice(0, startPart - 1);
1798
+ } else if (isAbsolute) {
1799
+ ret.dir = '/';
1800
+ }
1801
+
1802
+ return ret;
1803
+ },
1804
+
1805
+ sep: '/',
1806
+ delimiter: ':',
1807
+ win32: null,
1808
+ posix: null,
1809
+ };
1810
+
1811
+ posix.win32 = win32.win32 = win32;
1812
+ posix.posix = win32.posix = posix;
1813
+
1814
+ /**
1815
+ * Takes a Windows OS path and changes backward slashes to forward slashes.
1816
+ * This should only be done for OS paths from Windows (or user provided paths potentially from Windows).
1817
+ * Using it on a Linux or MaxOS path might change it.
1818
+ */
1819
+ export function toSlashes(osPath: string) {
1820
+ return osPath.replace(/[\\/]/g, posix.sep);
1821
+ }
1822
+
1823
+ export const normalize = process.platform === 'win32' ? win32.normalize : posix.normalize;
1824
+ export const isAbsolute = process.platform === 'win32' ? win32.isAbsolute : posix.isAbsolute;
1825
+ export const join = process.platform === 'win32' ? win32.join : posix.join;
1826
+ export const resolve = process.platform === 'win32' ? win32.resolve : posix.resolve;
1827
+ export const relative = process.platform === 'win32' ? win32.relative : posix.relative;
1828
+ export const dirname = process.platform === 'win32' ? win32.dirname : posix.dirname;
1829
+ export const basename = process.platform === 'win32' ? win32.basename : posix.basename;
1830
+ export const extname = process.platform === 'win32' ? win32.extname : posix.extname;
1831
+ export const format = process.platform === 'win32' ? win32.format : posix.format;
1832
+ export const parse = process.platform === 'win32' ? win32.parse : posix.parse;
1833
+ export const toNamespacedPath = process.platform === 'win32' ? win32.toNamespacedPath : posix.toNamespacedPath;
1834
+ export const sep = process.platform === 'win32' ? win32.sep : posix.sep;
1835
+ export const delimiter = process.platform === 'win32' ? win32.delimiter : posix.delimiter;
1836
+
1837
+ export function replaceAsarInPath(pathMayInAsar: string) {
1838
+ parse(pathMayInAsar);
1839
+ const parts = pathMayInAsar.split(normalize('/'));
1840
+ parts.forEach((part, i) => {
1841
+ if (part.endsWith('.asar')) {
1842
+ parts[i] = part + '.unpacked';
1843
+ }
1844
+ });
1845
+ return parts.join(normalize('/'));
1846
+ }
1847
+
1848
+ // Reference: https://en.wikipedia.org/wiki/Filename
1849
+ const WINDOWS_INVALID_FILE_CHARS = /[\\/:*?"<>|]/g;
1850
+ const UNIX_INVALID_FILE_CHARS = /[\\/]/g;
1851
+ const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i;
1852
+ export function isValidBasename(name: string | null | undefined, isWindowsOS: boolean = isWindows): boolean {
1853
+ const invalidFileChars = isWindowsOS ? WINDOWS_INVALID_FILE_CHARS : UNIX_INVALID_FILE_CHARS;
1854
+
1855
+ if (!name || name.length === 0 || /^\s+$/.test(name)) {
1856
+ return false; // require a name that is not just whitespace
1857
+ }
1858
+
1859
+ invalidFileChars.lastIndex = 0; // the holy grail of software development
1860
+ if (invalidFileChars.test(name)) {
1861
+ return false; // check for certain invalid file characters
1862
+ }
1863
+
1864
+ if (isWindowsOS && WINDOWS_FORBIDDEN_NAMES.test(name)) {
1865
+ return false; // check for certain invalid file names
1866
+ }
1867
+
1868
+ if (name === '.' || name === '..') {
1869
+ return false; // check for reserved values
1870
+ }
1871
+
1872
+ if (isWindowsOS && name[name.length - 1] === '.') {
1873
+ return false; // Windows: file cannot end with a "."
1874
+ }
1875
+
1876
+ if (isWindowsOS && name.length !== name.trim().length) {
1877
+ return false; // Windows: file cannot end with a whitespace
1878
+ }
1879
+
1880
+ if (name.length > 255) {
1881
+ return false; // most file systems do not allow files > 255 length
1882
+ }
1883
+
1884
+ return true;
1885
+ }