@symbiote-native/file-system 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/LICENSE +21 -0
- package/README.md +262 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +1 -0
- package/build/core/file-system.d.ts +169 -0
- package/build/core/file-system.js +404 -0
- package/build/core/index.d.ts +2 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +32 -0
- package/build/core/native-module.js +2 -0
- package/build/core/types.d.ts +104 -0
- package/build/core/types.js +15 -0
- package/build/next/directory.d.ts +36 -0
- package/build/next/directory.js +59 -0
- package/build/next/file.d.ts +71 -0
- package/build/next/file.js +207 -0
- package/build/next/index.d.ts +5 -0
- package/build/next/index.js +5 -0
- package/build/next/native-module.d.ts +109 -0
- package/build/next/native-module.js +2 -0
- package/build/next/network-tasks.d.ts +118 -0
- package/build/next/network-tasks.js +333 -0
- package/build/next/node-path.d.ts +16 -0
- package/build/next/node-path.js +353 -0
- package/build/next/path-utilities.d.ts +42 -0
- package/build/next/path-utilities.js +110 -0
- package/build/next/paths.d.ts +22 -0
- package/build/next/paths.js +43 -0
- package/build/next/streams.d.ts +25 -0
- package/build/next/streams.js +55 -0
- package/build/next/types.d.ts +152 -0
- package/build/next/types.js +19 -0
- package/build/next/url-utilities.d.ts +3 -0
- package/build/next/url-utilities.js +62 -0
- package/build/next/watcher.d.ts +19 -0
- package/build/next/watcher.js +64 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +2 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/next/directory.d.ts +36 -0
- package/build-ngc/next/directory.js +60 -0
- package/build-ngc/next/directory.js.map +1 -0
- package/build-ngc/next/file.d.ts +71 -0
- package/build-ngc/next/file.js +208 -0
- package/build-ngc/next/file.js.map +1 -0
- package/build-ngc/next/index.d.ts +5 -0
- package/build-ngc/next/index.js +6 -0
- package/build-ngc/next/index.js.map +1 -0
- package/build-ngc/next/native-module.d.ts +109 -0
- package/build-ngc/next/native-module.js +3 -0
- package/build-ngc/next/native-module.js.map +1 -0
- package/build-ngc/next/network-tasks.d.ts +118 -0
- package/build-ngc/next/network-tasks.js +334 -0
- package/build-ngc/next/network-tasks.js.map +1 -0
- package/build-ngc/next/node-path.d.ts +16 -0
- package/build-ngc/next/node-path.js +354 -0
- package/build-ngc/next/node-path.js.map +1 -0
- package/build-ngc/next/path-utilities.d.ts +42 -0
- package/build-ngc/next/path-utilities.js +111 -0
- package/build-ngc/next/path-utilities.js.map +1 -0
- package/build-ngc/next/paths.d.ts +22 -0
- package/build-ngc/next/paths.js +44 -0
- package/build-ngc/next/paths.js.map +1 -0
- package/build-ngc/next/streams.d.ts +25 -0
- package/build-ngc/next/streams.js +56 -0
- package/build-ngc/next/streams.js.map +1 -0
- package/build-ngc/next/types.d.ts +152 -0
- package/build-ngc/next/types.js +20 -0
- package/build-ngc/next/types.js.map +1 -0
- package/build-ngc/next/url-utilities.d.ts +3 -0
- package/build-ngc/next/url-utilities.js +63 -0
- package/build-ngc/next/url-utilities.js.map +1 -0
- package/build-ngc/next/watcher.d.ts +19 -0
- package/build-ngc/next/watcher.js +65 -0
- package/build-ngc/next/watcher.js.map +1 -0
- package/native-link.json +17 -0
- package/package.json +154 -0
- package/src/angular/index.ts +1 -0
- package/src/core/file-system.ts +634 -0
- package/src/core/index.ts +2 -0
- package/src/core/native-module.ts +99 -0
- package/src/core/types.ts +135 -0
- package/src/next/directory.ts +83 -0
- package/src/next/file.ts +318 -0
- package/src/next/index.ts +5 -0
- package/src/next/native-module.ts +188 -0
- package/src/next/network-tasks.ts +456 -0
- package/src/next/node-path.ts +362 -0
- package/src/next/path-utilities.ts +144 -0
- package/src/next/paths.ts +52 -0
- package/src/next/streams-ambient.d.ts +34 -0
- package/src/next/streams.ts +81 -0
- package/src/next/types.ts +194 -0
- package/src/next/url-utilities.ts +70 -0
- package/src/next/watcher.ts +106 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
// Ported verbatim from expo-file-system's own vendored copy of Node's posix `path` module
|
|
2
|
+
// (Copyright Joyent, Inc. and other Node contributors, MIT licensed) — a self-contained,
|
|
3
|
+
// mechanical algorithm with zero native dependency, so a straight port carries low risk.
|
|
4
|
+
|
|
5
|
+
function isPathSeparator(code: string) {
|
|
6
|
+
return code === '/';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Resolves . and .. elements in a path with directory names
|
|
10
|
+
function normalizeString(
|
|
11
|
+
path: string,
|
|
12
|
+
allowAboveRoot: boolean,
|
|
13
|
+
separator: string,
|
|
14
|
+
) {
|
|
15
|
+
let res = '';
|
|
16
|
+
let lastSegmentLength = 0;
|
|
17
|
+
let lastSlash = -1;
|
|
18
|
+
let dots = 0;
|
|
19
|
+
let code = '';
|
|
20
|
+
for (let i = 0; i <= path.length; ++i) {
|
|
21
|
+
if (i < path.length) code = path.charAt(i);
|
|
22
|
+
else if (isPathSeparator(code)) break;
|
|
23
|
+
else code = '/';
|
|
24
|
+
|
|
25
|
+
if (isPathSeparator(code)) {
|
|
26
|
+
if (lastSlash === i - 1 || dots === 1) {
|
|
27
|
+
// NOOP
|
|
28
|
+
} else if (dots === 2) {
|
|
29
|
+
if (
|
|
30
|
+
res.length < 2 ||
|
|
31
|
+
lastSegmentLength !== 2 ||
|
|
32
|
+
res.charAt(res.length - 1) !== '.' ||
|
|
33
|
+
res.charAt(res.length - 2) !== '.'
|
|
34
|
+
) {
|
|
35
|
+
if (res.length > 2) {
|
|
36
|
+
const lastSlashIndex = res.lastIndexOf(separator);
|
|
37
|
+
if (lastSlashIndex === -1) {
|
|
38
|
+
res = '';
|
|
39
|
+
lastSegmentLength = 0;
|
|
40
|
+
} else {
|
|
41
|
+
res = res.slice(0, lastSlashIndex);
|
|
42
|
+
lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
|
|
43
|
+
}
|
|
44
|
+
lastSlash = i;
|
|
45
|
+
dots = 0;
|
|
46
|
+
continue;
|
|
47
|
+
} else if (res.length !== 0) {
|
|
48
|
+
res = '';
|
|
49
|
+
lastSegmentLength = 0;
|
|
50
|
+
lastSlash = i;
|
|
51
|
+
dots = 0;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (allowAboveRoot) {
|
|
56
|
+
res += res.length > 0 ? `${separator}..` : '..';
|
|
57
|
+
lastSegmentLength = 2;
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
if (res.length > 0)
|
|
61
|
+
res += `${separator}${path.slice(lastSlash + 1, i)}`;
|
|
62
|
+
else res = path.slice(lastSlash + 1, i);
|
|
63
|
+
lastSegmentLength = i - lastSlash - 1;
|
|
64
|
+
}
|
|
65
|
+
lastSlash = i;
|
|
66
|
+
dots = 0;
|
|
67
|
+
} else if (code === '.' && dots !== -1) {
|
|
68
|
+
++dots;
|
|
69
|
+
} else {
|
|
70
|
+
dots = -1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return res;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function resolve(...args: string[]) {
|
|
77
|
+
let resolvedPath = '';
|
|
78
|
+
let resolvedAbsolute = false;
|
|
79
|
+
|
|
80
|
+
for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) {
|
|
81
|
+
const path = i >= 0 ? args[i] : '';
|
|
82
|
+
|
|
83
|
+
if (!path || path.length === 0) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
88
|
+
resolvedAbsolute = !!(path && path.charAt(0) === '/');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, '/');
|
|
92
|
+
|
|
93
|
+
if (resolvedAbsolute) {
|
|
94
|
+
return `/${resolvedPath}`;
|
|
95
|
+
}
|
|
96
|
+
return resolvedPath.length > 0 ? resolvedPath : '.';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function normalize(path: string) {
|
|
100
|
+
if (path.length === 0) return '.';
|
|
101
|
+
|
|
102
|
+
const isAbsolute = path.charAt(0) === '/';
|
|
103
|
+
const trailingSeparator = path.charAt(path.length - 1) === '/';
|
|
104
|
+
|
|
105
|
+
path = normalizeString(path, !isAbsolute, '/');
|
|
106
|
+
|
|
107
|
+
if (path.length === 0) {
|
|
108
|
+
if (isAbsolute) return '/';
|
|
109
|
+
return trailingSeparator ? './' : '.';
|
|
110
|
+
}
|
|
111
|
+
if (trailingSeparator) path += '/';
|
|
112
|
+
|
|
113
|
+
return isAbsolute ? `/${path}` : path;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function isAbsolute(path: string) {
|
|
117
|
+
return path.length > 0 && path.charAt(0) === '/';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function join(...args: string[]) {
|
|
121
|
+
if (args.length === 0) return '.';
|
|
122
|
+
|
|
123
|
+
const path: string[] = [];
|
|
124
|
+
for (let i = 0; i < args.length; ++i) {
|
|
125
|
+
const arg = args[i];
|
|
126
|
+
if (arg && arg.length > 0) {
|
|
127
|
+
path.push(arg);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (path.length === 0) return '.';
|
|
132
|
+
|
|
133
|
+
return normalize(path.join('/'));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function relative(from: string, to: string) {
|
|
137
|
+
if (from === to) return '';
|
|
138
|
+
|
|
139
|
+
from = resolve(from);
|
|
140
|
+
to = resolve(to);
|
|
141
|
+
|
|
142
|
+
if (from === to) return '';
|
|
143
|
+
|
|
144
|
+
const fromStart = 1;
|
|
145
|
+
const fromEnd = from.length;
|
|
146
|
+
const fromLen = fromEnd - fromStart;
|
|
147
|
+
const toStart = 1;
|
|
148
|
+
const toLen = to.length - toStart;
|
|
149
|
+
|
|
150
|
+
const length = fromLen < toLen ? fromLen : toLen;
|
|
151
|
+
let lastCommonSep = -1;
|
|
152
|
+
let i = 0;
|
|
153
|
+
for (; i < length; i++) {
|
|
154
|
+
const fromChar = from.charAt(fromStart + i);
|
|
155
|
+
if (fromChar !== to.charAt(toStart + i)) break;
|
|
156
|
+
else if (fromChar === '/') lastCommonSep = i;
|
|
157
|
+
}
|
|
158
|
+
if (i === length) {
|
|
159
|
+
if (toLen > length) {
|
|
160
|
+
if (to.charAt(toStart + i) === '/') {
|
|
161
|
+
return to.slice(toStart + i + 1);
|
|
162
|
+
}
|
|
163
|
+
if (i === 0) {
|
|
164
|
+
return to.slice(toStart + i);
|
|
165
|
+
}
|
|
166
|
+
} else if (fromLen > length) {
|
|
167
|
+
if (from.charAt(fromStart + i) === '/') {
|
|
168
|
+
lastCommonSep = i;
|
|
169
|
+
} else if (i === 0) {
|
|
170
|
+
lastCommonSep = 0;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
let out = '';
|
|
176
|
+
for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
|
|
177
|
+
if (i === fromEnd || from.charAt(i) === '/') {
|
|
178
|
+
out += out.length === 0 ? '..' : '/..';
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return `${out}${to.slice(toStart + lastCommonSep)}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function dirname(path: string) {
|
|
186
|
+
if (path.length === 0) return '.';
|
|
187
|
+
const hasRoot = path.charAt(0) === '/';
|
|
188
|
+
let end = -1;
|
|
189
|
+
let matchedSlash = true;
|
|
190
|
+
for (let i = path.length - 1; i >= 1; --i) {
|
|
191
|
+
if (path.charAt(i) === '/') {
|
|
192
|
+
if (!matchedSlash) {
|
|
193
|
+
end = i;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
matchedSlash = false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (end === -1) return hasRoot ? '/' : '.';
|
|
202
|
+
if (hasRoot && end === 1) return '//';
|
|
203
|
+
return path.slice(0, end);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function basename(path: string, suffix?: string) {
|
|
207
|
+
let start = 0;
|
|
208
|
+
let end = -1;
|
|
209
|
+
let matchedSlash = true;
|
|
210
|
+
|
|
211
|
+
if (
|
|
212
|
+
suffix !== undefined &&
|
|
213
|
+
suffix.length > 0 &&
|
|
214
|
+
suffix.length <= path.length
|
|
215
|
+
) {
|
|
216
|
+
if (suffix === path) return '';
|
|
217
|
+
let extIdx = suffix.length - 1;
|
|
218
|
+
let firstNonSlashEnd = -1;
|
|
219
|
+
for (let i = path.length - 1; i >= 0; --i) {
|
|
220
|
+
const code = path.charAt(i);
|
|
221
|
+
if (code === '/') {
|
|
222
|
+
if (!matchedSlash) {
|
|
223
|
+
start = i + 1;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
if (firstNonSlashEnd === -1) {
|
|
228
|
+
matchedSlash = false;
|
|
229
|
+
firstNonSlashEnd = i + 1;
|
|
230
|
+
}
|
|
231
|
+
if (extIdx >= 0) {
|
|
232
|
+
if (code === suffix.charAt(extIdx)) {
|
|
233
|
+
if (--extIdx === -1) {
|
|
234
|
+
end = i;
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
extIdx = -1;
|
|
238
|
+
end = firstNonSlashEnd;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (start === end) end = firstNonSlashEnd;
|
|
245
|
+
else if (end === -1) end = path.length;
|
|
246
|
+
return path.slice(start, end);
|
|
247
|
+
}
|
|
248
|
+
for (let i = path.length - 1; i >= 0; --i) {
|
|
249
|
+
if (path.charAt(i) === '/') {
|
|
250
|
+
if (!matchedSlash) {
|
|
251
|
+
start = i + 1;
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
} else if (end === -1) {
|
|
255
|
+
matchedSlash = false;
|
|
256
|
+
end = i + 1;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (end === -1) return '';
|
|
261
|
+
return path.slice(start, end);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function extname(path: string) {
|
|
265
|
+
let startDot = -1;
|
|
266
|
+
let startPart = 0;
|
|
267
|
+
let end = -1;
|
|
268
|
+
let matchedSlash = true;
|
|
269
|
+
let preDotState = 0;
|
|
270
|
+
for (let i = path.length - 1; i >= 0; --i) {
|
|
271
|
+
const code = path.charAt(i);
|
|
272
|
+
if (code === '/') {
|
|
273
|
+
if (!matchedSlash) {
|
|
274
|
+
startPart = i + 1;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (end === -1) {
|
|
280
|
+
matchedSlash = false;
|
|
281
|
+
end = i + 1;
|
|
282
|
+
}
|
|
283
|
+
if (code === '.') {
|
|
284
|
+
if (startDot === -1) startDot = i;
|
|
285
|
+
else if (preDotState !== 1) preDotState = 1;
|
|
286
|
+
} else if (startDot !== -1) {
|
|
287
|
+
preDotState = -1;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (
|
|
292
|
+
startDot === -1 ||
|
|
293
|
+
end === -1 ||
|
|
294
|
+
preDotState === 0 ||
|
|
295
|
+
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
|
|
296
|
+
) {
|
|
297
|
+
return '';
|
|
298
|
+
}
|
|
299
|
+
return path.slice(startDot, end);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function parse(path: string) {
|
|
303
|
+
const ret = { root: '', dir: '', base: '', ext: '', name: '' };
|
|
304
|
+
if (path.length === 0) return ret;
|
|
305
|
+
const isAbsolutePath = path.charAt(0) === '/';
|
|
306
|
+
let start;
|
|
307
|
+
if (isAbsolutePath) {
|
|
308
|
+
ret.root = '/';
|
|
309
|
+
start = 1;
|
|
310
|
+
} else {
|
|
311
|
+
start = 0;
|
|
312
|
+
}
|
|
313
|
+
let startDot = -1;
|
|
314
|
+
let startPart = 0;
|
|
315
|
+
let end = -1;
|
|
316
|
+
let matchedSlash = true;
|
|
317
|
+
let i = path.length - 1;
|
|
318
|
+
let preDotState = 0;
|
|
319
|
+
|
|
320
|
+
for (; i >= start; --i) {
|
|
321
|
+
const code = path.charAt(i);
|
|
322
|
+
if (code === '/') {
|
|
323
|
+
if (!matchedSlash) {
|
|
324
|
+
startPart = i + 1;
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (end === -1) {
|
|
330
|
+
matchedSlash = false;
|
|
331
|
+
end = i + 1;
|
|
332
|
+
}
|
|
333
|
+
if (code === '.') {
|
|
334
|
+
if (startDot === -1) startDot = i;
|
|
335
|
+
else if (preDotState !== 1) preDotState = 1;
|
|
336
|
+
} else if (startDot !== -1) {
|
|
337
|
+
preDotState = -1;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (end !== -1) {
|
|
342
|
+
const nameStart = startPart === 0 && isAbsolutePath ? 1 : startPart;
|
|
343
|
+
if (
|
|
344
|
+
startDot === -1 ||
|
|
345
|
+
preDotState === 0 ||
|
|
346
|
+
(preDotState === 1 && startDot === end - 1 && startDot === startPart + 1)
|
|
347
|
+
) {
|
|
348
|
+
ret.base = ret.name = path.slice(nameStart, end);
|
|
349
|
+
} else {
|
|
350
|
+
ret.name = path.slice(nameStart, startDot);
|
|
351
|
+
ret.base = path.slice(nameStart, end);
|
|
352
|
+
ret.ext = path.slice(startDot, end);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (startPart > 0) ret.dir = path.slice(0, startPart - 1);
|
|
357
|
+
else if (isAbsolutePath) ret.dir = '/';
|
|
358
|
+
|
|
359
|
+
return ret;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export const sep = '/';
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { File } from './file';
|
|
2
|
+
import type { Directory } from './directory';
|
|
3
|
+
import * as nodePath from './node-path';
|
|
4
|
+
import { asUrl, isUrl, encodeURLChars } from './url-utilities';
|
|
5
|
+
|
|
6
|
+
function uriObjectToString(path: string | File | Directory): string {
|
|
7
|
+
return typeof path === 'string' ? path : path.uri;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// React Native's own ambient `URL` type (react-native/src/types/globals.d.ts) declares every
|
|
11
|
+
// property but `search` as readonly, so a new pathname is applied by reconstructing the URL
|
|
12
|
+
// rather than assigning `.pathname` in place — matching what a real setter would do internally.
|
|
13
|
+
// Built from `.protocol`/`.host` rather than `.origin`, because `file://` URLs report `.origin`
|
|
14
|
+
// as the literal string `"null"` per spec.
|
|
15
|
+
function withPathname(url: URL, pathname: string): URL {
|
|
16
|
+
return new URL(
|
|
17
|
+
`${url.protocol}//${url.host}${pathname}${url.search}${url.hash}`,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class PathUtilities {
|
|
22
|
+
/**
|
|
23
|
+
* Joins path segments into a single path.
|
|
24
|
+
*/
|
|
25
|
+
static join(...paths: (string | File | Directory)[]): string {
|
|
26
|
+
const [firstSegment = '', ...rest] = paths.map(uriObjectToString);
|
|
27
|
+
const pathAsUrl = asUrl(firstSegment);
|
|
28
|
+
if (pathAsUrl) {
|
|
29
|
+
const joined = nodePath.join(
|
|
30
|
+
pathAsUrl.pathname,
|
|
31
|
+
...rest.map(encodeURLChars),
|
|
32
|
+
);
|
|
33
|
+
return withPathname(pathAsUrl, joined).toString();
|
|
34
|
+
}
|
|
35
|
+
return nodePath.join(firstSegment, ...rest.map(encodeURLChars));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolves a relative path to an absolute path.
|
|
40
|
+
*/
|
|
41
|
+
static relative(
|
|
42
|
+
from: string | File | Directory,
|
|
43
|
+
to: string | File | Directory,
|
|
44
|
+
): string {
|
|
45
|
+
let fromPath: string | File | Directory = from;
|
|
46
|
+
let toPath: string | File | Directory = to;
|
|
47
|
+
const fromString = uriObjectToString(from);
|
|
48
|
+
const toString = uriObjectToString(to);
|
|
49
|
+
|
|
50
|
+
if (isUrl(fromString)) {
|
|
51
|
+
fromPath = asUrl(fromString)!.pathname;
|
|
52
|
+
}
|
|
53
|
+
if (isUrl(toString)) {
|
|
54
|
+
toPath = asUrl(toString)!.pathname;
|
|
55
|
+
}
|
|
56
|
+
return nodePath.relative(
|
|
57
|
+
uriObjectToString(fromPath),
|
|
58
|
+
uriObjectToString(toPath),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Checks if a path is absolute.
|
|
64
|
+
*/
|
|
65
|
+
static isAbsolute(path: string | File | Directory): boolean {
|
|
66
|
+
const pathString = uriObjectToString(path);
|
|
67
|
+
if (isUrl(pathString)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
return nodePath.isAbsolute(pathString);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Normalizes a path.
|
|
75
|
+
*/
|
|
76
|
+
static normalize(path: string | File | Directory): string {
|
|
77
|
+
const pathString = uriObjectToString(path);
|
|
78
|
+
const pathURL = asUrl(encodeURLChars(pathString));
|
|
79
|
+
if (pathURL) {
|
|
80
|
+
const normalized = encodeURLChars(
|
|
81
|
+
nodePath.normalize(decodeURIComponent(pathURL.pathname)),
|
|
82
|
+
);
|
|
83
|
+
return withPathname(pathURL, normalized).toString();
|
|
84
|
+
}
|
|
85
|
+
return nodePath.normalize(pathString);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Returns the directory name of a path.
|
|
90
|
+
*/
|
|
91
|
+
static dirname(path: string | File | Directory): string {
|
|
92
|
+
const pathString = uriObjectToString(path);
|
|
93
|
+
const pathURL = asUrl(pathString);
|
|
94
|
+
if (pathURL) {
|
|
95
|
+
const dirnamePath = encodeURLChars(
|
|
96
|
+
nodePath.dirname(decodeURIComponent(pathURL.pathname)),
|
|
97
|
+
);
|
|
98
|
+
return withPathname(pathURL, dirnamePath).toString();
|
|
99
|
+
}
|
|
100
|
+
return nodePath.dirname(pathString);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Returns the base name of a path.
|
|
105
|
+
*/
|
|
106
|
+
static basename(path: string | File | Directory, ext?: string): string {
|
|
107
|
+
const pathString = uriObjectToString(path);
|
|
108
|
+
const pathURL = asUrl(pathString);
|
|
109
|
+
if (pathURL) {
|
|
110
|
+
return nodePath.basename(decodeURIComponent(pathURL.pathname));
|
|
111
|
+
}
|
|
112
|
+
return nodePath.basename(pathString, ext);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns the extension of a path.
|
|
117
|
+
*/
|
|
118
|
+
static extname(path: string | File | Directory): string {
|
|
119
|
+
const pathString = uriObjectToString(path);
|
|
120
|
+
const pathURL = asUrl(pathString);
|
|
121
|
+
if (pathURL) {
|
|
122
|
+
return nodePath.extname(decodeURIComponent(pathURL.pathname));
|
|
123
|
+
}
|
|
124
|
+
return nodePath.extname(pathString);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Parses a path into its components.
|
|
129
|
+
*/
|
|
130
|
+
static parse(path: string | File | Directory): {
|
|
131
|
+
root: string;
|
|
132
|
+
dir: string;
|
|
133
|
+
base: string;
|
|
134
|
+
ext: string;
|
|
135
|
+
name: string;
|
|
136
|
+
} {
|
|
137
|
+
const pathString = uriObjectToString(path);
|
|
138
|
+
const pathURL = asUrl(pathString);
|
|
139
|
+
if (pathURL) {
|
|
140
|
+
return nodePath.parse(decodeURIComponent(pathURL.pathname));
|
|
141
|
+
}
|
|
142
|
+
return nodePath.parse(pathString);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Directory } from './directory';
|
|
2
|
+
import { expoFileSystemNext } from './native-module';
|
|
3
|
+
import { PathUtilities } from './path-utilities';
|
|
4
|
+
import type { IPathInfo } from './types';
|
|
5
|
+
|
|
6
|
+
export class Paths extends PathUtilities {
|
|
7
|
+
/**
|
|
8
|
+
* A place to store files that can be deleted by the system when the device runs low on
|
|
9
|
+
* storage.
|
|
10
|
+
*/
|
|
11
|
+
static get cache() {
|
|
12
|
+
return new Directory(expoFileSystemNext.cacheDirectory);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** The directory where assets bundled with the application are stored. */
|
|
16
|
+
static get bundle() {
|
|
17
|
+
return new Directory(expoFileSystemNext.bundleDirectory);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A place to store files that are safe from being deleted by the system. */
|
|
21
|
+
static get document() {
|
|
22
|
+
return new Directory(expoFileSystemNext.documentDirectory);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** @platform ios */
|
|
26
|
+
static get appleSharedContainers() {
|
|
27
|
+
const containers: Record<string, string> =
|
|
28
|
+
expoFileSystemNext.appleSharedContainers ?? {};
|
|
29
|
+
const result: Record<string, Directory> = {};
|
|
30
|
+
for (const appGroupId in containers) {
|
|
31
|
+
if (containers[appGroupId]) {
|
|
32
|
+
result[appGroupId] = new Directory(containers[appGroupId]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The total space on the device's internal storage, in bytes. */
|
|
39
|
+
static get totalDiskSpace() {
|
|
40
|
+
return expoFileSystemNext.totalDiskSpace;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The available space on the device's internal storage, in bytes. */
|
|
44
|
+
static get availableDiskSpace() {
|
|
45
|
+
return expoFileSystemNext.availableDiskSpace;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Returns an object that indicates if the specified path represents a directory. */
|
|
49
|
+
static info(...uris: string[]): IPathInfo {
|
|
50
|
+
return expoFileSystemNext.info(uris.join('/'));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// React Native ships its own ambient global types (react-native/src/types/globals.d.ts) that
|
|
2
|
+
// this project's tsconfig picks up ahead of any DOM lib, and that file declares `Blob`, `File`,
|
|
3
|
+
// `Response`, `AbortSignal`, `URL` (all readonly except `search` — see path-utilities.ts's own
|
|
4
|
+
// note), but NOT `ReadableStream`/`WritableStream` at all, and its `AbortSignal` class predates
|
|
5
|
+
// the spec's `reason` property. RN's own Streams runtime (react-native/flow/streams.js.flow) has
|
|
6
|
+
// no TypeScript counterpart. These are minimal structural declarations for the shapes this
|
|
7
|
+
// package's `/next` surface actually constructs and reads — not a full spec-complete polyfill.
|
|
8
|
+
|
|
9
|
+
declare global {
|
|
10
|
+
class ReadableStream<R = unknown> {
|
|
11
|
+
constructor(underlyingSource?: unknown, strategy?: unknown);
|
|
12
|
+
readonly locked: boolean;
|
|
13
|
+
cancel(reason?: unknown): Promise<void>;
|
|
14
|
+
getReader(): unknown;
|
|
15
|
+
pipeThrough(transform: unknown, options?: unknown): ReadableStream;
|
|
16
|
+
pipeTo(destination: unknown, options?: unknown): Promise<void>;
|
|
17
|
+
tee(): [ReadableStream<R>, ReadableStream<R>];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
21
|
+
class WritableStream<W = unknown> {
|
|
22
|
+
constructor(underlyingSink?: unknown, strategy?: unknown);
|
|
23
|
+
readonly locked: boolean;
|
|
24
|
+
abort(reason?: unknown): Promise<void>;
|
|
25
|
+
close(): Promise<void>;
|
|
26
|
+
getWriter(): unknown;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface AbortSignal {
|
|
30
|
+
readonly reason?: unknown;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { IFileSystemHandle } from './types';
|
|
2
|
+
|
|
3
|
+
// This project's tsconfig carries no DOM lib (RN ships its own Streams polyfill, typed only in
|
|
4
|
+
// Flow upstream — react-native/flow/streams.js.flow — with no TypeScript ambient declaration
|
|
5
|
+
// reachable here), so the WHATWG `UnderlyingByteSource`/`UnderlyingSink`/
|
|
6
|
+
// `ReadableByteStreamController` names aren't in scope. These local structural types match the
|
|
7
|
+
// spec shape `new ReadableStream(source)`/`new WritableStream(sink)` expect at the call site.
|
|
8
|
+
type IReadableByteStreamController = {
|
|
9
|
+
readonly byobRequest: {
|
|
10
|
+
view: ArrayBufferView | null;
|
|
11
|
+
respond(bytesWritten: number): void;
|
|
12
|
+
} | null;
|
|
13
|
+
close(): void;
|
|
14
|
+
enqueue(chunk: Uint8Array): void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class FileSystemReadableStreamSource {
|
|
18
|
+
handle: IFileSystemHandle;
|
|
19
|
+
size: number = 1024;
|
|
20
|
+
type = 'bytes' as const;
|
|
21
|
+
|
|
22
|
+
constructor(handle: IFileSystemHandle) {
|
|
23
|
+
this.handle = handle;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
cancel() {
|
|
27
|
+
this.handle.close();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pull(controller: IReadableByteStreamController) {
|
|
31
|
+
const theView = controller.byobRequest?.view;
|
|
32
|
+
if (!theView) {
|
|
33
|
+
const bytes = this.handle.readBytes(this.size);
|
|
34
|
+
if (bytes.length === 0) {
|
|
35
|
+
controller.close();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
controller.enqueue(bytes);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// TODO: Optimize by adding a native method that can write into a TypedArray at a given offset.
|
|
43
|
+
const bytes = this.handle.readBytes(
|
|
44
|
+
theView.byteLength - theView.byteOffset,
|
|
45
|
+
);
|
|
46
|
+
if (bytes.length === 0) {
|
|
47
|
+
controller.close();
|
|
48
|
+
controller.byobRequest.respond(0);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (theView instanceof Uint8Array) {
|
|
52
|
+
theView.set(bytes, theView.byteOffset);
|
|
53
|
+
} else {
|
|
54
|
+
const array = new Uint8Array(theView.buffer);
|
|
55
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
56
|
+
array[i + (theView.byteOffset ?? 0)] = bytes[i]!;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
controller.byobRequest.respond(bytes.length);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class FileSystemWritableSink {
|
|
64
|
+
handle: IFileSystemHandle;
|
|
65
|
+
|
|
66
|
+
constructor(handle: IFileSystemHandle) {
|
|
67
|
+
this.handle = handle;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
abort() {
|
|
71
|
+
this.close();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
close() {
|
|
75
|
+
this.handle.close();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
write(chunk: Uint8Array) {
|
|
79
|
+
this.handle.writeBytes(chunk);
|
|
80
|
+
}
|
|
81
|
+
}
|