@gjsify/path 0.0.1 → 0.1.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.
- package/README.md +26 -2
- package/lib/esm/constants.js +20 -0
- package/lib/esm/index.js +52 -0
- package/lib/esm/posix.js +339 -0
- package/lib/esm/util.js +113 -0
- package/lib/esm/win32.js +573 -0
- package/lib/types/constants.d.ts +9 -0
- package/lib/types/index.d.ts +23 -0
- package/lib/types/posix.d.ts +21 -0
- package/lib/types/util.d.ts +12 -0
- package/lib/types/win32.d.ts +21 -0
- package/package.json +35 -17
- package/src/constants.ts +11 -0
- package/src/index.spec.ts +964 -0
- package/src/index.ts +47 -0
- package/src/posix.ts +406 -0
- package/src/test.mts +6 -0
- package/src/util.ts +145 -0
- package/src/win32.ts +651 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/index.js +0 -75
package/src/index.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Reference: Node.js lib/path.js
|
|
2
|
+
// Reimplemented for GJS
|
|
3
|
+
// Exports POSIX implementation by default (GJS runs on POSIX systems)
|
|
4
|
+
|
|
5
|
+
import * as posix from './posix.js';
|
|
6
|
+
import * as win32 from './win32.js';
|
|
7
|
+
|
|
8
|
+
export type { ParsedPath, FormatInputPathObject } from './posix.js';
|
|
9
|
+
|
|
10
|
+
// Re-export all POSIX functions as default path API
|
|
11
|
+
export const {
|
|
12
|
+
resolve,
|
|
13
|
+
normalize,
|
|
14
|
+
isAbsolute,
|
|
15
|
+
join,
|
|
16
|
+
relative,
|
|
17
|
+
toNamespacedPath,
|
|
18
|
+
dirname,
|
|
19
|
+
basename,
|
|
20
|
+
extname,
|
|
21
|
+
format,
|
|
22
|
+
parse,
|
|
23
|
+
sep,
|
|
24
|
+
delimiter,
|
|
25
|
+
} = posix;
|
|
26
|
+
|
|
27
|
+
// Export platform-specific implementations
|
|
28
|
+
export { posix, win32 };
|
|
29
|
+
|
|
30
|
+
// Default export is the posix module (matching Node.js behavior on POSIX systems)
|
|
31
|
+
export default {
|
|
32
|
+
resolve: posix.resolve,
|
|
33
|
+
normalize: posix.normalize,
|
|
34
|
+
isAbsolute: posix.isAbsolute,
|
|
35
|
+
join: posix.join,
|
|
36
|
+
relative: posix.relative,
|
|
37
|
+
toNamespacedPath: posix.toNamespacedPath,
|
|
38
|
+
dirname: posix.dirname,
|
|
39
|
+
basename: posix.basename,
|
|
40
|
+
extname: posix.extname,
|
|
41
|
+
format: posix.format,
|
|
42
|
+
parse: posix.parse,
|
|
43
|
+
sep: posix.sep,
|
|
44
|
+
delimiter: posix.delimiter,
|
|
45
|
+
posix,
|
|
46
|
+
win32,
|
|
47
|
+
};
|
package/src/posix.ts
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Adapted from Deno (refs/deno/ext/node/polyfills/path/_posix.ts) and Node.js (refs/node/lib/path.js)
|
|
3
|
+
// Copyright (c) 2018-2026 the Deno authors. MIT license.
|
|
4
|
+
// Copyright (c) Node.js contributors. MIT license.
|
|
5
|
+
// Modifications: TypeScript types, no primordials, GJS-compatible
|
|
6
|
+
|
|
7
|
+
import { CHAR_DOT, CHAR_FORWARD_SLASH } from './constants.js';
|
|
8
|
+
import {
|
|
9
|
+
assertPath,
|
|
10
|
+
isPosixPathSeparator,
|
|
11
|
+
normalizeString,
|
|
12
|
+
_format,
|
|
13
|
+
} from './util.js';
|
|
14
|
+
|
|
15
|
+
export interface ParsedPath {
|
|
16
|
+
root: string;
|
|
17
|
+
dir: string;
|
|
18
|
+
base: string;
|
|
19
|
+
ext: string;
|
|
20
|
+
name: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type FormatInputPathObject = Partial<ParsedPath>;
|
|
24
|
+
|
|
25
|
+
export const sep = '/';
|
|
26
|
+
export const delimiter = ':';
|
|
27
|
+
|
|
28
|
+
function posixCwd(): string {
|
|
29
|
+
// In GJS, try GLib.get_current_dir() at runtime
|
|
30
|
+
if (typeof globalThis.process?.cwd === 'function') {
|
|
31
|
+
return globalThis.process.cwd();
|
|
32
|
+
}
|
|
33
|
+
// Fallback: try GLib
|
|
34
|
+
try {
|
|
35
|
+
const GLib = (globalThis as any).imports?.gi?.GLib;
|
|
36
|
+
if (GLib?.get_current_dir) {
|
|
37
|
+
return GLib.get_current_dir();
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
// ignore
|
|
41
|
+
}
|
|
42
|
+
return '/';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolve(...pathSegments: string[]): string {
|
|
46
|
+
let resolvedPath = '';
|
|
47
|
+
let resolvedAbsolute = false;
|
|
48
|
+
|
|
49
|
+
for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
|
50
|
+
let path: string;
|
|
51
|
+
if (i >= 0) {
|
|
52
|
+
path = pathSegments[i];
|
|
53
|
+
assertPath(path);
|
|
54
|
+
if (path.length === 0) continue;
|
|
55
|
+
} else {
|
|
56
|
+
path = posixCwd();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
60
|
+
resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/', isPosixPathSeparator);
|
|
64
|
+
|
|
65
|
+
if (resolvedAbsolute) {
|
|
66
|
+
if (resolvedPath.length > 0) {
|
|
67
|
+
return `/${resolvedPath}`;
|
|
68
|
+
}
|
|
69
|
+
return '/';
|
|
70
|
+
} else if (resolvedPath.length > 0) {
|
|
71
|
+
return resolvedPath;
|
|
72
|
+
}
|
|
73
|
+
return '.';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function normalize(path: string): string {
|
|
77
|
+
assertPath(path);
|
|
78
|
+
|
|
79
|
+
if (path.length === 0) return '.';
|
|
80
|
+
|
|
81
|
+
const isAbsolutePath = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
|
82
|
+
const trailingSeparator = path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH;
|
|
83
|
+
|
|
84
|
+
let normalized = normalizeString(path, !isAbsolutePath, '/', isPosixPathSeparator);
|
|
85
|
+
|
|
86
|
+
if (normalized.length === 0 && !isAbsolutePath) {
|
|
87
|
+
normalized = '.';
|
|
88
|
+
}
|
|
89
|
+
if (normalized.length > 0 && trailingSeparator) {
|
|
90
|
+
normalized += '/';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (isAbsolutePath) {
|
|
94
|
+
return `/${normalized}`;
|
|
95
|
+
}
|
|
96
|
+
return normalized;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function isAbsolute(path: string): boolean {
|
|
100
|
+
assertPath(path);
|
|
101
|
+
return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function join(...paths: string[]): string {
|
|
105
|
+
if (paths.length === 0) return '.';
|
|
106
|
+
|
|
107
|
+
let joined: string | undefined;
|
|
108
|
+
for (let i = 0; i < paths.length; ++i) {
|
|
109
|
+
const arg = paths[i];
|
|
110
|
+
assertPath(arg);
|
|
111
|
+
if (arg.length > 0) {
|
|
112
|
+
if (joined === undefined) {
|
|
113
|
+
joined = arg;
|
|
114
|
+
} else {
|
|
115
|
+
joined += `/${arg}`;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (joined === undefined) return '.';
|
|
120
|
+
return normalize(joined);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function relative(from: string, to: string): string {
|
|
124
|
+
assertPath(from);
|
|
125
|
+
assertPath(to);
|
|
126
|
+
|
|
127
|
+
if (from === to) return '';
|
|
128
|
+
|
|
129
|
+
from = resolve(from);
|
|
130
|
+
to = resolve(to);
|
|
131
|
+
|
|
132
|
+
if (from === to) return '';
|
|
133
|
+
|
|
134
|
+
// Find common prefix
|
|
135
|
+
let fromStart = 1;
|
|
136
|
+
const fromEnd = from.length;
|
|
137
|
+
const fromLen = fromEnd - fromStart;
|
|
138
|
+
|
|
139
|
+
let toStart = 1;
|
|
140
|
+
const toLen = to.length - toStart;
|
|
141
|
+
|
|
142
|
+
const length = fromLen < toLen ? fromLen : toLen;
|
|
143
|
+
let lastCommonSep = -1;
|
|
144
|
+
let i = 0;
|
|
145
|
+
|
|
146
|
+
for (; i <= length; ++i) {
|
|
147
|
+
if (i === length) {
|
|
148
|
+
if (toLen > length) {
|
|
149
|
+
if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) {
|
|
150
|
+
return to.slice(toStart + i + 1);
|
|
151
|
+
} else if (i === 0) {
|
|
152
|
+
return to.slice(toStart + i);
|
|
153
|
+
}
|
|
154
|
+
} else if (fromLen > length) {
|
|
155
|
+
if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) {
|
|
156
|
+
lastCommonSep = i;
|
|
157
|
+
} else if (i === 0) {
|
|
158
|
+
lastCommonSep = 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
const fromCode = from.charCodeAt(fromStart + i);
|
|
164
|
+
const toCode = to.charCodeAt(toStart + i);
|
|
165
|
+
if (fromCode !== toCode) break;
|
|
166
|
+
if (fromCode === CHAR_FORWARD_SLASH) lastCommonSep = i;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let out = '';
|
|
170
|
+
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
|
|
171
|
+
if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) {
|
|
172
|
+
if (out.length === 0) {
|
|
173
|
+
out += '..';
|
|
174
|
+
} else {
|
|
175
|
+
out += '/..';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (out.length > 0) {
|
|
181
|
+
return out + to.slice(toStart + lastCommonSep);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
toStart += lastCommonSep;
|
|
185
|
+
if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) {
|
|
186
|
+
++toStart;
|
|
187
|
+
}
|
|
188
|
+
return to.slice(toStart);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function toNamespacedPath(path: string): string {
|
|
192
|
+
// On POSIX, this is a no-op
|
|
193
|
+
return path;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function dirname(path: string): string {
|
|
197
|
+
assertPath(path);
|
|
198
|
+
if (path.length === 0) return '.';
|
|
199
|
+
|
|
200
|
+
const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
|
201
|
+
let end = -1;
|
|
202
|
+
let matchedSlash = true;
|
|
203
|
+
|
|
204
|
+
for (let i = path.length - 1; i >= 1; --i) {
|
|
205
|
+
if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
|
|
206
|
+
if (!matchedSlash) {
|
|
207
|
+
end = i;
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
matchedSlash = false;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (end === -1) return hasRoot ? '/' : '.';
|
|
216
|
+
if (hasRoot && end === 1) return '//';
|
|
217
|
+
return path.slice(0, end);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function basename(path: string, ext?: string): string {
|
|
221
|
+
if (ext !== undefined) assertPath(ext);
|
|
222
|
+
assertPath(path);
|
|
223
|
+
|
|
224
|
+
let start = 0;
|
|
225
|
+
let end = -1;
|
|
226
|
+
let matchedSlash = true;
|
|
227
|
+
|
|
228
|
+
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
|
|
229
|
+
if (ext.length === path.length && ext === path) return '';
|
|
230
|
+
let extIdx = ext.length - 1;
|
|
231
|
+
let firstNonSlashEnd = -1;
|
|
232
|
+
for (let i = path.length - 1; i >= 0; --i) {
|
|
233
|
+
const code = path.charCodeAt(i);
|
|
234
|
+
if (code === CHAR_FORWARD_SLASH) {
|
|
235
|
+
if (!matchedSlash) {
|
|
236
|
+
start = i + 1;
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
if (firstNonSlashEnd === -1) {
|
|
241
|
+
matchedSlash = false;
|
|
242
|
+
firstNonSlashEnd = i + 1;
|
|
243
|
+
}
|
|
244
|
+
if (extIdx >= 0) {
|
|
245
|
+
if (code === ext.charCodeAt(extIdx)) {
|
|
246
|
+
if (--extIdx === -1) {
|
|
247
|
+
end = i;
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
extIdx = -1;
|
|
251
|
+
end = firstNonSlashEnd;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (start === end) end = firstNonSlashEnd;
|
|
258
|
+
else if (end === -1) end = path.length;
|
|
259
|
+
return path.slice(start, end);
|
|
260
|
+
} else {
|
|
261
|
+
for (let i = path.length - 1; i >= 0; --i) {
|
|
262
|
+
if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) {
|
|
263
|
+
if (!matchedSlash) {
|
|
264
|
+
start = i + 1;
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
} else if (end === -1) {
|
|
268
|
+
matchedSlash = false;
|
|
269
|
+
end = i + 1;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (end === -1) return '';
|
|
274
|
+
return path.slice(start, end);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function extname(path: string): string {
|
|
279
|
+
assertPath(path);
|
|
280
|
+
|
|
281
|
+
let startDot = -1;
|
|
282
|
+
let startPart = 0;
|
|
283
|
+
let end = -1;
|
|
284
|
+
let matchedSlash = true;
|
|
285
|
+
let preDotState = 0;
|
|
286
|
+
|
|
287
|
+
for (let i = path.length - 1; i >= 0; --i) {
|
|
288
|
+
const code = path.charCodeAt(i);
|
|
289
|
+
if (code === CHAR_FORWARD_SLASH) {
|
|
290
|
+
if (!matchedSlash) {
|
|
291
|
+
startPart = i + 1;
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (end === -1) {
|
|
297
|
+
matchedSlash = false;
|
|
298
|
+
end = i + 1;
|
|
299
|
+
}
|
|
300
|
+
if (code === CHAR_DOT) {
|
|
301
|
+
if (startDot === -1) {
|
|
302
|
+
startDot = i;
|
|
303
|
+
} else if (preDotState !== 1) {
|
|
304
|
+
preDotState = 1;
|
|
305
|
+
}
|
|
306
|
+
} else if (startDot !== -1) {
|
|
307
|
+
preDotState = -1;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (
|
|
312
|
+
startDot === -1 ||
|
|
313
|
+
end === -1 ||
|
|
314
|
+
preDotState === 0 ||
|
|
315
|
+
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
|
|
316
|
+
) {
|
|
317
|
+
return '';
|
|
318
|
+
}
|
|
319
|
+
return path.slice(startDot, end);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function format(pathObject: FormatInputPathObject): string {
|
|
323
|
+
if (pathObject === null || typeof pathObject !== 'object') {
|
|
324
|
+
throw new TypeError(
|
|
325
|
+
'The "pathObject" argument must be of type Object. Received type ' + typeof pathObject
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
return _format('/', pathObject);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function parse(path: string): ParsedPath {
|
|
332
|
+
assertPath(path);
|
|
333
|
+
|
|
334
|
+
const ret: ParsedPath = { root: '', dir: '', base: '', ext: '', name: '' };
|
|
335
|
+
if (path.length === 0) return ret;
|
|
336
|
+
|
|
337
|
+
const isAbsolutePath = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
|
|
338
|
+
let start: number;
|
|
339
|
+
|
|
340
|
+
if (isAbsolutePath) {
|
|
341
|
+
ret.root = '/';
|
|
342
|
+
start = 1;
|
|
343
|
+
} else {
|
|
344
|
+
start = 0;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
let startDot = -1;
|
|
348
|
+
let startPart = 0;
|
|
349
|
+
let end = -1;
|
|
350
|
+
let matchedSlash = true;
|
|
351
|
+
let i = path.length - 1;
|
|
352
|
+
let preDotState = 0;
|
|
353
|
+
|
|
354
|
+
for (; i >= start; --i) {
|
|
355
|
+
const code = path.charCodeAt(i);
|
|
356
|
+
if (code === CHAR_FORWARD_SLASH) {
|
|
357
|
+
if (!matchedSlash) {
|
|
358
|
+
startPart = i + 1;
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
if (end === -1) {
|
|
364
|
+
matchedSlash = false;
|
|
365
|
+
end = i + 1;
|
|
366
|
+
}
|
|
367
|
+
if (code === CHAR_DOT) {
|
|
368
|
+
if (startDot === -1) startDot = i;
|
|
369
|
+
else if (preDotState !== 1) preDotState = 1;
|
|
370
|
+
} else if (startDot !== -1) {
|
|
371
|
+
preDotState = -1;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (
|
|
376
|
+
startDot === -1 ||
|
|
377
|
+
end === -1 ||
|
|
378
|
+
preDotState === 0 ||
|
|
379
|
+
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
|
|
380
|
+
) {
|
|
381
|
+
if (end !== -1) {
|
|
382
|
+
if (startPart === 0 && isAbsolutePath) {
|
|
383
|
+
ret.base = ret.name = path.slice(1, end);
|
|
384
|
+
} else {
|
|
385
|
+
ret.base = ret.name = path.slice(startPart, end);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} else {
|
|
389
|
+
if (startPart === 0 && isAbsolutePath) {
|
|
390
|
+
ret.name = path.slice(1, startDot);
|
|
391
|
+
ret.base = path.slice(1, end);
|
|
392
|
+
} else {
|
|
393
|
+
ret.name = path.slice(startPart, startDot);
|
|
394
|
+
ret.base = path.slice(startPart, end);
|
|
395
|
+
}
|
|
396
|
+
ret.ext = path.slice(startDot, end);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (startPart > 0) {
|
|
400
|
+
ret.dir = path.slice(0, startPart - 1);
|
|
401
|
+
} else if (isAbsolutePath) {
|
|
402
|
+
ret.dir = '/';
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return ret;
|
|
406
|
+
}
|
package/src/test.mts
ADDED
package/src/util.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Adapted from Deno (refs/deno/ext/node/polyfills/path/) and Node.js (refs/node/lib/path.js)
|
|
3
|
+
// Copyright (c) 2018-2026 the Deno authors. MIT license.
|
|
4
|
+
// Copyright (c) Node.js contributors. MIT license.
|
|
5
|
+
// Modifications: TypeScript types, no primordials
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
CHAR_DOT,
|
|
9
|
+
CHAR_FORWARD_SLASH,
|
|
10
|
+
CHAR_BACKWARD_SLASH,
|
|
11
|
+
} from './constants.js';
|
|
12
|
+
|
|
13
|
+
export function assertPath(path: unknown): asserts path is string {
|
|
14
|
+
if (typeof path !== 'string') {
|
|
15
|
+
throw new TypeError(
|
|
16
|
+
'The "path" argument must be of type string. Received type ' + typeof path
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isPosixPathSeparator(code: number): boolean {
|
|
22
|
+
return code === CHAR_FORWARD_SLASH;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isPathSeparator(code: number): boolean {
|
|
26
|
+
return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isWindowsDeviceRoot(code: number): boolean {
|
|
30
|
+
return (
|
|
31
|
+
(code >= 65 && code <= 90) || // A-Z
|
|
32
|
+
(code >= 97 && code <= 122) // a-z
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolves `.` and `..` segments in a path string.
|
|
38
|
+
*/
|
|
39
|
+
export function normalizeString(
|
|
40
|
+
path: string,
|
|
41
|
+
allowAboveRoot: boolean,
|
|
42
|
+
separator: string,
|
|
43
|
+
isPathSep: (code: number) => boolean
|
|
44
|
+
): string {
|
|
45
|
+
let res = '';
|
|
46
|
+
let lastSegmentLength = 0;
|
|
47
|
+
let lastSlash = -1;
|
|
48
|
+
let dots = 0;
|
|
49
|
+
let code: number;
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i <= path.length; ++i) {
|
|
52
|
+
if (i < path.length) {
|
|
53
|
+
code = path.charCodeAt(i);
|
|
54
|
+
} else if (isPathSep(code!)) {
|
|
55
|
+
break;
|
|
56
|
+
} else {
|
|
57
|
+
code = CHAR_FORWARD_SLASH;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (isPathSep(code)) {
|
|
61
|
+
if (lastSlash === i - 1 || dots === 1) {
|
|
62
|
+
// NOOP — skip consecutive separators or single dot
|
|
63
|
+
} else if (lastSlash !== i - 1 && dots === 2) {
|
|
64
|
+
if (
|
|
65
|
+
res.length < 2 ||
|
|
66
|
+
lastSegmentLength !== 2 ||
|
|
67
|
+
res.charCodeAt(res.length - 1) !== CHAR_DOT ||
|
|
68
|
+
res.charCodeAt(res.length - 2) !== CHAR_DOT
|
|
69
|
+
) {
|
|
70
|
+
if (res.length > 2) {
|
|
71
|
+
const lastSlashIndex = res.lastIndexOf(separator);
|
|
72
|
+
if (lastSlashIndex === -1) {
|
|
73
|
+
res = '';
|
|
74
|
+
lastSegmentLength = 0;
|
|
75
|
+
} else {
|
|
76
|
+
res = res.slice(0, lastSlashIndex);
|
|
77
|
+
lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
|
|
78
|
+
}
|
|
79
|
+
lastSlash = i;
|
|
80
|
+
dots = 0;
|
|
81
|
+
continue;
|
|
82
|
+
} else if (res.length === 2 || res.length === 1) {
|
|
83
|
+
res = '';
|
|
84
|
+
lastSegmentLength = 0;
|
|
85
|
+
lastSlash = i;
|
|
86
|
+
dots = 0;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (allowAboveRoot) {
|
|
91
|
+
if (res.length > 0) {
|
|
92
|
+
res += `${separator}..`;
|
|
93
|
+
} else {
|
|
94
|
+
res = '..';
|
|
95
|
+
}
|
|
96
|
+
lastSegmentLength = 2;
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
if (res.length > 0) {
|
|
100
|
+
res += separator + path.slice(lastSlash + 1, i);
|
|
101
|
+
} else {
|
|
102
|
+
res = path.slice(lastSlash + 1, i);
|
|
103
|
+
}
|
|
104
|
+
lastSegmentLength = i - lastSlash - 1;
|
|
105
|
+
}
|
|
106
|
+
lastSlash = i;
|
|
107
|
+
dots = 0;
|
|
108
|
+
} else if (code === CHAR_DOT && dots !== -1) {
|
|
109
|
+
++dots;
|
|
110
|
+
} else {
|
|
111
|
+
dots = -1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return res;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Format a parsed path object into a path string.
|
|
120
|
+
*/
|
|
121
|
+
export function _format(sep: string, pathObject: Record<string, any>): string {
|
|
122
|
+
if (pathObject === null || typeof pathObject !== 'object') {
|
|
123
|
+
throw new TypeError(
|
|
124
|
+
'The "pathObject" argument must be of type Object. Received type ' + typeof pathObject
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const dir = pathObject.dir || pathObject.root;
|
|
129
|
+
const base =
|
|
130
|
+
pathObject.base || (pathObject.name || '') + formatExt(pathObject.ext);
|
|
131
|
+
|
|
132
|
+
if (!dir) {
|
|
133
|
+
return base;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (dir === pathObject.root) {
|
|
137
|
+
return dir + base;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return dir + sep + base;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function formatExt(ext?: string): string {
|
|
144
|
+
return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : '';
|
|
145
|
+
}
|