forked from codesandbox/codesandbox-client
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutil.ts
More file actions
256 lines (237 loc) · 7.63 KB
/
util.ts
File metadata and controls
256 lines (237 loc) · 7.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/**
* Grab bag of utility functions used across the code.
*/
import {FileSystem, BFSOneArgCallback, FileSystemConstructor} from './file_system';
import {ErrorCode, ApiError} from './api_error';
import levenshtein from './levenshtein';
import * as path from 'path';
export function deprecationMessage(print: boolean, fsName: string, opts: any): void {
if (print) {
// tslint:disable-next-line:no-console
console.warn(`[${fsName}] Direct file system constructor usage is deprecated for this file system, and will be removed in the next major version. Please use the '${fsName}.Create(${JSON.stringify(opts)}, callback)' method instead. See https://github.com/jvilk/BrowserFS/issues/176 for more details.`);
// tslint:enable-next-line:no-console
}
}
/**
* Checks for any IE version, including IE11 which removed MSIE from the
* userAgent string.
* @hidden
*/
export const isIE: boolean = typeof navigator !== "undefined" && Boolean(/(msie) ([\w.]+)/.exec(navigator.userAgent.toLowerCase()) || navigator.userAgent.indexOf('Trident') !== -1);
/**
* Check if we're in a web worker.
* @hidden
*/
export const isWebWorker: boolean = typeof window === "undefined";
/**
* @hidden
*/
export interface Arrayish<T> {
[idx: number]: T;
length: number;
}
/**
* Throws an exception. Called on code paths that should be impossible.
* @hidden
*/
export function fail() {
throw new Error("BFS has reached an impossible code path; please file a bug.");
}
/**
* Synchronous recursive makedir.
* @hidden
*/
export function mkdirpSync(p: string, mode: number, fs: FileSystem): void {
if (!fs.existsSync(p)) {
mkdirpSync(path.dirname(p), mode, fs);
fs.mkdirSync(p, mode);
}
}
/**
* Converts a buffer into an array buffer. Attempts to do so in a
* zero-copy manner, e.g. the array references the same memory.
* @hidden
*/
export function buffer2ArrayBuffer(buff: Buffer): ArrayBuffer | SharedArrayBuffer {
const u8 = buffer2Uint8array(buff),
u8offset = u8.byteOffset,
u8Len = u8.byteLength;
if (u8offset === 0 && u8Len === u8.buffer.byteLength) {
return u8.buffer;
} else {
return u8.buffer.slice(u8offset, u8offset + u8Len);
}
}
/**
* Converts a buffer into a Uint8Array. Attempts to do so in a
* zero-copy manner, e.g. the array references the same memory.
* @hidden
*/
export function buffer2Uint8array(buff: Buffer): Uint8Array {
if (buff instanceof Uint8Array) {
// BFS & Node v4.0 buffers *are* Uint8Arrays.
return <any> buff;
} else {
// Uint8Arrays can be constructed from arrayish numbers.
// At this point, we assume this isn't a BFS array.
return new Uint8Array(buff);
}
}
/**
* Converts the given arrayish object into a Buffer. Attempts to
* be zero-copy.
* @hidden
*/
export function arrayish2Buffer(arr: Arrayish<number>): Buffer {
if (arr instanceof Buffer) {
return arr;
} else if (arr instanceof Uint8Array) {
return uint8Array2Buffer(arr);
} else {
return Buffer.from(<number[]> arr);
}
}
/**
* Converts the given Uint8Array into a Buffer. Attempts to be zero-copy.
* @hidden
*/
export function uint8Array2Buffer(u8: Uint8Array): Buffer {
if (u8 instanceof Buffer) {
return u8;
} else if (u8.byteOffset === 0 && u8.byteLength === u8.buffer.byteLength) {
return arrayBuffer2Buffer(u8.buffer);
} else {
return Buffer.from(<ArrayBuffer> u8.buffer, u8.byteOffset, u8.byteLength);
}
}
/**
* Converts the given array buffer into a Buffer. Attempts to be
* zero-copy.
* @hidden
*/
export function arrayBuffer2Buffer(ab: ArrayBuffer | SharedArrayBuffer): Buffer {
return Buffer.from(<ArrayBuffer> ab);
}
/**
* Copies a slice of the given buffer
* @hidden
*/
export function copyingSlice(buff: Buffer, start: number = 0, end = buff.length): Buffer {
if (start < 0 || end < 0 || end > buff.length || start > end) {
throw new TypeError(`Invalid slice bounds on buffer of length ${buff.length}: [${start}, ${end}]`);
}
if (buff.length === 0) {
// Avoid s0 corner case in ArrayBuffer case.
return emptyBuffer();
} else {
const u8 = buffer2Uint8array(buff),
s0 = buff[0],
newS0 = (s0 + 1) % 0xFF;
buff[0] = newS0;
if (u8[0] === newS0) {
// Same memory. Revert & copy.
u8[0] = s0;
return uint8Array2Buffer(u8.slice(start, end));
} else {
// Revert.
buff[0] = s0;
return uint8Array2Buffer(u8.subarray(start, end));
}
}
}
/**
* @hidden
*/
let emptyBuff: Buffer | null = null;
/**
* Returns an empty buffer.
* @hidden
*/
export function emptyBuffer(): Buffer {
if (emptyBuff) {
return emptyBuff;
}
return emptyBuff = Buffer.alloc(0);
}
/**
* Option validator for a Buffer file system option.
* @hidden
*/
export function bufferValidator(v: object, cb: BFSOneArgCallback): void {
if (Buffer.isBuffer(v)) {
cb();
} else {
cb(new ApiError(ErrorCode.EINVAL, `option must be a Buffer.`));
}
}
/**
* Checks that the given options object is valid for the file system options.
* @hidden
*/
export function checkOptions(fsType: FileSystemConstructor, opts: any, cb: BFSOneArgCallback): void {
const optsInfo = fsType.Options;
const fsName = fsType.Name;
let pendingValidators = 0;
let callbackCalled = false;
let loopEnded = false;
function validatorCallback(e?: ApiError): void {
if (!callbackCalled) {
if (e) {
callbackCalled = true;
cb(e);
}
pendingValidators--;
if (pendingValidators === 0 && loopEnded) {
cb();
}
}
}
// Check for required options.
for (const optName in optsInfo) {
if (optsInfo.hasOwnProperty(optName)) {
const opt = optsInfo[optName];
const providedValue = opts[optName];
if (providedValue === undefined || providedValue === null) {
if (!opt.optional) {
// Required option, not provided.
// Any incorrect options provided? Which ones are close to the provided one?
// (edit distance 5 === close)
const incorrectOptions = Object.keys(opts).filter((o) => !(o in optsInfo)).map((a: string) => {
return {str: a, distance: levenshtein(optName, a)};
}).filter((o) => o.distance < 5).sort((a, b) => a.distance - b.distance);
// Validators may be synchronous.
if (callbackCalled) {
return;
}
callbackCalled = true;
return cb(new ApiError(ErrorCode.EINVAL, `[${fsName}] Required option '${optName}' not provided.${incorrectOptions.length > 0 ? ` You provided unrecognized option '${incorrectOptions[0].str}'; perhaps you meant to type '${optName}'.` : ''}\nOption description: ${opt.description}`));
}
// Else: Optional option, not provided. That is OK.
} else {
// Option provided! Check type.
let typeMatches = false;
if (Array.isArray(opt.type)) {
typeMatches = opt.type.indexOf(typeof(providedValue)) !== -1;
} else {
typeMatches = typeof(providedValue) === opt.type;
}
if (!typeMatches) {
// Validators may be synchronous.
if (callbackCalled) {
return;
}
callbackCalled = true;
return cb(new ApiError(ErrorCode.EINVAL, `[${fsName}] Value provided for option ${optName} is not the proper type. Expected ${Array.isArray(opt.type) ? `one of {${opt.type.join(", ")}}` : opt.type}, but received ${typeof(providedValue)}\nOption description: ${opt.description}`));
} else if (opt.validator) {
pendingValidators++;
opt.validator(providedValue, validatorCallback);
}
// Otherwise: All good!
}
}
}
loopEnded = true;
if (pendingValidators === 0 && !callbackCalled) {
cb();
}
}