@environment-safe/file 0.0.1
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/.babelrc.cjs +18 -0
- package/.eslintrc.cjs +42 -0
- package/.husky/pre-commit +11 -0
- package/.import-config.json +5 -0
- package/.jsdoc.json +15 -0
- package/LICENSE +20 -0
- package/README.md +95 -0
- package/containers/development.dockerfile +11 -0
- package/containers/production.dockerfile +11 -0
- package/containers/test.dockerfile +11 -0
- package/dist/buffer.cjs +77 -0
- package/dist/index.cjs +599 -0
- package/index.html +74 -0
- package/package.json +107 -0
- package/src/buffer.mjs +73 -0
- package/src/index.mjs +599 -0
- package/test/index.html +443 -0
- package/test/lauch-insecure-chrome +20 -0
- package/test/test.cjs +10 -0
- package/test/test.mjs +59 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.save = exports.remove = exports.pathJoin = exports.load = exports.listFiles = exports.list = exports.info = exports.handle = exports.fileBody = exports.exists = exports.File = void 0;
|
|
7
|
+
var _browserOrNode = require("browser-or-node");
|
|
8
|
+
var _buffer = require("./buffer.cjs");
|
|
9
|
+
var fs = _interopRequireWildcard(require("fs"));
|
|
10
|
+
var path = _interopRequireWildcard(require("path"));
|
|
11
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
12
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
|
+
/*
|
|
14
|
+
import { isBrowser, isJsDom } from 'browser-or-node';
|
|
15
|
+
import * as mod from 'module';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
let internalRequire = null;
|
|
18
|
+
if(typeof require !== 'undefined') internalRequire = require;
|
|
19
|
+
const ensureRequire = ()=> (!internalRequire) && (internalRequire = mod.createRequire(import.meta.url));
|
|
20
|
+
//*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A JSON object
|
|
24
|
+
* @typedef { object } JSON
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const inputQueue = [];
|
|
28
|
+
const attachInputGenerator = eventType => {
|
|
29
|
+
const handler = event => {
|
|
30
|
+
if (inputQueue.length) {
|
|
31
|
+
const input = inputQueue.shift();
|
|
32
|
+
try {
|
|
33
|
+
input.handler(event, input.resolve, input.reject);
|
|
34
|
+
} catch (ex) {
|
|
35
|
+
inputQueue.unshift(input);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
window.addEventListener('load', event => {
|
|
40
|
+
document.body.addEventListener(eventType, handler, false);
|
|
41
|
+
});
|
|
42
|
+
//document.body.addEventListener(eventType, handler, false);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
46
|
+
attachInputGenerator('mousedown');
|
|
47
|
+
// mousemove is cleanest, but seems unreliable
|
|
48
|
+
// attachInputGenerator('mousemove');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const wantInput = async (id, handler, cache) => {
|
|
52
|
+
const promise = new Promise((resolve, reject) => {
|
|
53
|
+
inputQueue.push({
|
|
54
|
+
resolve,
|
|
55
|
+
reject,
|
|
56
|
+
handler
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
const input = await promise;
|
|
60
|
+
return await input;
|
|
61
|
+
};
|
|
62
|
+
const getFilePickerOptions = (name, path) => {
|
|
63
|
+
let suffix = name.split('.').pop();
|
|
64
|
+
if (suffix.length > 6) suffix = '';
|
|
65
|
+
const options = {
|
|
66
|
+
suggestedName: name
|
|
67
|
+
};
|
|
68
|
+
if (path) options.startIn = path;
|
|
69
|
+
if (suffix) {
|
|
70
|
+
const accept = {};
|
|
71
|
+
accept[mimesBySuffix[suffix]] = '.' + suffix;
|
|
72
|
+
options.types = [{
|
|
73
|
+
description: suffix,
|
|
74
|
+
accept
|
|
75
|
+
}];
|
|
76
|
+
options.excludeAcceptAllOption = true;
|
|
77
|
+
}
|
|
78
|
+
return options;
|
|
79
|
+
};
|
|
80
|
+
const makeLocation = (path, dir) => {
|
|
81
|
+
if (dir && dir[0] === '.') {
|
|
82
|
+
if (dir[1] === '.') {
|
|
83
|
+
if (dir[2] === '/' && dir[3]) {
|
|
84
|
+
return pathJoin(File.directory.current, '..', dir.substring(3), path);
|
|
85
|
+
} else {
|
|
86
|
+
if (dir[2]) {
|
|
87
|
+
return pathJoin(File.directory.current, '..', dir.substring(3), path);
|
|
88
|
+
} else {
|
|
89
|
+
return pathJoin(File.directory.current, '..', path);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
if (dir[1] === '/') {
|
|
94
|
+
return pathJoin(File.directory.current, dir.substring(2), path);
|
|
95
|
+
} else {
|
|
96
|
+
if (dir[1]) {
|
|
97
|
+
return pathJoin(File.directory.current, dir, path);
|
|
98
|
+
} else {
|
|
99
|
+
return pathJoin(File.directory.current, path);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return dir ? handleCanonicalPath(dir, File.os, File.user) + '/' + path : path;
|
|
105
|
+
};
|
|
106
|
+
const save = async (name, dir, buffer, meta = {}) => {
|
|
107
|
+
const location = makeLocation(name, dir);
|
|
108
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
109
|
+
const options = getFilePickerOptions(name, dir);
|
|
110
|
+
const newHandle = await wantInput(location, (event, resolve, reject) => {
|
|
111
|
+
try {
|
|
112
|
+
window.showSaveFilePicker(options).then(thisHandle => {
|
|
113
|
+
resolve(thisHandle);
|
|
114
|
+
}).catch(ex => {
|
|
115
|
+
reject(ex);
|
|
116
|
+
});
|
|
117
|
+
} catch (ex) {
|
|
118
|
+
reject(ex);
|
|
119
|
+
}
|
|
120
|
+
}, meta.cache);
|
|
121
|
+
const writableStream = await newHandle.createWritable();
|
|
122
|
+
// write our file
|
|
123
|
+
await writableStream.write(buffer);
|
|
124
|
+
// close the file and write the contents to disk.
|
|
125
|
+
await writableStream.close();
|
|
126
|
+
} else {
|
|
127
|
+
return await new Promise((resolve, reject) => {
|
|
128
|
+
fs.writeFile(location, buffer, err => {
|
|
129
|
+
if (err) return reject(err);
|
|
130
|
+
resolve();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
exports.save = save;
|
|
136
|
+
const mimesBySuffix = {
|
|
137
|
+
json: 'application/json',
|
|
138
|
+
jpg: 'image/jpeg',
|
|
139
|
+
jpeg: 'image/jpeg',
|
|
140
|
+
gif: 'image/gif',
|
|
141
|
+
png: 'image/png',
|
|
142
|
+
svg: 'image/sxg+xml',
|
|
143
|
+
webp: 'image/webp',
|
|
144
|
+
csv: 'text/csv',
|
|
145
|
+
tsv: 'text/tsv',
|
|
146
|
+
ssv: 'text/ssv',
|
|
147
|
+
js: 'text/javascript',
|
|
148
|
+
mjs: 'text/javascript',
|
|
149
|
+
cjs: 'text/javascript',
|
|
150
|
+
css: 'text/css'
|
|
151
|
+
};
|
|
152
|
+
const pathJoin = (...parts) => {
|
|
153
|
+
//returns buffer, eventually stream
|
|
154
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
155
|
+
return parts.join('/');
|
|
156
|
+
} else {
|
|
157
|
+
return path.join.apply(path, parts);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
//todo: should I remove export (no one should use this)?
|
|
162
|
+
exports.pathJoin = pathJoin;
|
|
163
|
+
const fileBody = async (path, dir, baseDir, allowRedirect, forceReturn) => {
|
|
164
|
+
try {
|
|
165
|
+
//let location = dir?dir+ '/' + path:path; //todo: looser handling
|
|
166
|
+
let location = makeLocation(path, dir);
|
|
167
|
+
if (canonicalLocationToPath['darwin'][dir]) {
|
|
168
|
+
if (baseDir) {
|
|
169
|
+
throw new Error('custom directories unsupported');
|
|
170
|
+
} else {
|
|
171
|
+
location = 'file://' + handleCanonicalPath(dir, File.os, File.user) + '/' + path;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const response = await fetch(location);
|
|
175
|
+
const text = await response.text();
|
|
176
|
+
if (!(response.ok || allowRedirect && response.redirected || forceReturn)) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return text;
|
|
180
|
+
} catch (ex) {
|
|
181
|
+
//console.log(location, ex);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
exports.fileBody = fileBody;
|
|
186
|
+
const handle = async (path, dir, writable, cache = {}) => {
|
|
187
|
+
//returns buffer, eventually stream
|
|
188
|
+
let suffix = path.split('.').pop();
|
|
189
|
+
if (suffix.length > 6) suffix = '';
|
|
190
|
+
const location = makeLocation(path, dir);
|
|
191
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
192
|
+
if (cache && cache[path]) return cache[path];
|
|
193
|
+
const options = getFilePickerOptions(path);
|
|
194
|
+
try {
|
|
195
|
+
const response = await fileBody(path, dir);
|
|
196
|
+
if (response === null) throw new Error('File not found');
|
|
197
|
+
} catch (ex) {
|
|
198
|
+
const newHandle = await wantInput(location, (event, resolve, reject) => {
|
|
199
|
+
try {
|
|
200
|
+
window.showSaveFilePicker(options).then(thisHandle => {
|
|
201
|
+
resolve(thisHandle);
|
|
202
|
+
}).catch(ex => {
|
|
203
|
+
reject(ex);
|
|
204
|
+
});
|
|
205
|
+
} catch (ex) {
|
|
206
|
+
reject(ex);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
return newHandle;
|
|
210
|
+
}
|
|
211
|
+
const fileHandle = await wantInput(location, (event, resolve, reject) => {
|
|
212
|
+
try {
|
|
213
|
+
window.showOpenFilePicker(options).then(([handle]) => {
|
|
214
|
+
resolve(handle);
|
|
215
|
+
}).catch(ex => {
|
|
216
|
+
reject(ex);
|
|
217
|
+
});
|
|
218
|
+
} catch (ex) {
|
|
219
|
+
reject(ex);
|
|
220
|
+
}
|
|
221
|
+
}, cache);
|
|
222
|
+
// eslint-disable-next-line no-undef
|
|
223
|
+
if (cache) cache[location] = fileHandle;
|
|
224
|
+
// eslint-disable-next-line no-undef
|
|
225
|
+
return fileHandle;
|
|
226
|
+
} else {
|
|
227
|
+
// todo: impl
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
exports.handle = handle;
|
|
231
|
+
const load = async (path, dir, cache) => {
|
|
232
|
+
//returns buffer, eventually stream
|
|
233
|
+
const location = makeLocation(path, dir);
|
|
234
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
235
|
+
try {
|
|
236
|
+
const response = await fetch(location);
|
|
237
|
+
if (!response) {
|
|
238
|
+
return new ArrayBuffer();
|
|
239
|
+
}
|
|
240
|
+
const buffer = await response.arrayBuffer();
|
|
241
|
+
buffer;
|
|
242
|
+
return buffer;
|
|
243
|
+
} catch (ex) {
|
|
244
|
+
return new ArrayBuffer();
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
return await new Promise((resolve, reject) => {
|
|
248
|
+
fs.readFile(location, (err, body) => {
|
|
249
|
+
if (err) return reject(err);
|
|
250
|
+
resolve(body);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
exports.load = load;
|
|
256
|
+
const exists = async (path, dir, cache, incomingHandle) => {
|
|
257
|
+
//returns buffer, eventually stream
|
|
258
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
259
|
+
if (incomingHandle) {
|
|
260
|
+
const fileHandle = incomingHandle;
|
|
261
|
+
const file = await fileHandle.getFile();
|
|
262
|
+
const buffer = await file.arrayBuffer();
|
|
263
|
+
return !!buffer;
|
|
264
|
+
} else {
|
|
265
|
+
const body = await fileBody(path, dir);
|
|
266
|
+
return body !== null;
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
return await new Promise((resolve, reject) => {
|
|
270
|
+
const location = makeLocation(path, dir);
|
|
271
|
+
fs.stat(location, (err, res) => {
|
|
272
|
+
if (err) resolve(false);
|
|
273
|
+
resolve(true);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
exports.exists = exists;
|
|
279
|
+
const remove = async (path, dir, cache) => {
|
|
280
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
281
|
+
const fileHandle = await handle(path, dir, true, cache);
|
|
282
|
+
if (fileHandle.remove) fileHandle.remove(); //non-standard, but supported
|
|
283
|
+
} else {
|
|
284
|
+
// todo: impl
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
exports.remove = remove;
|
|
288
|
+
const info = async (path, dir, cache) => {
|
|
289
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
290
|
+
// todo: impl
|
|
291
|
+
} else {
|
|
292
|
+
// todo: impl
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
exports.info = info;
|
|
296
|
+
const list = async (path, options = {}) => {
|
|
297
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
298
|
+
// todo: impl
|
|
299
|
+
switch (File.agent.name) {
|
|
300
|
+
case 'chrome':
|
|
301
|
+
{
|
|
302
|
+
const page = await fileBody('', path, null, null, true);
|
|
303
|
+
let rows = page && page.match(/<script>addRow\((.*)\);<\/script>/g) || [];
|
|
304
|
+
rows = rows.map(row => {
|
|
305
|
+
return row.match(/<script>addRow\((.*)\);<\/script>/)[1];
|
|
306
|
+
});
|
|
307
|
+
const jsonData = `[[${rows.join('], [')}]]`;
|
|
308
|
+
const data = JSON.parse(jsonData);
|
|
309
|
+
let results = data.map(meta => {
|
|
310
|
+
return {
|
|
311
|
+
name: meta[0],
|
|
312
|
+
isFile: () => {
|
|
313
|
+
return !!meta[2];
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
});
|
|
317
|
+
if (Object.keys(options).length) {
|
|
318
|
+
if (options.files === false) {
|
|
319
|
+
results = results.filter(file => {
|
|
320
|
+
return !file.isFile();
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (options.directories === false) {
|
|
324
|
+
results = results.filter(file => {
|
|
325
|
+
return file.isFile();
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (!options.hidden) {
|
|
329
|
+
results = results.filter(file => {
|
|
330
|
+
return file !== '.' && file !== '..';
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return results.map(file => {
|
|
335
|
+
return file.name;
|
|
336
|
+
});
|
|
337
|
+
//TODO: apache fallback
|
|
338
|
+
//break;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
default:
|
|
342
|
+
throw new Error(`Usupported Browser: ${File.os}`);
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
//todo: platform safe separator
|
|
346
|
+
const target = path.indexOf('/') === -1 ? makeLocation('', path) : path;
|
|
347
|
+
return await new Promise((resolve, reject) => {
|
|
348
|
+
fs.readdir(target, {
|
|
349
|
+
withFileTypes: true
|
|
350
|
+
}, (err, files) => {
|
|
351
|
+
if (err) return reject(err);
|
|
352
|
+
let results = files;
|
|
353
|
+
if (Object.keys(options).length) {
|
|
354
|
+
if (options.files === false) {
|
|
355
|
+
results = results.filter(file => {
|
|
356
|
+
return !file.isFile();
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
if (options.directories === false) {
|
|
360
|
+
results = results.filter(file => {
|
|
361
|
+
return file.isFile();
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
if (!options.hidden) {
|
|
365
|
+
results = results.filter(file => {
|
|
366
|
+
return file !== '.' && file !== '..';
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
resolve(results.map(file => {
|
|
371
|
+
return file.name;
|
|
372
|
+
}));
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
exports.list = list;
|
|
378
|
+
const internalCache = {};
|
|
379
|
+
const listFiles = path => {
|
|
380
|
+
return list(path).map(src => {
|
|
381
|
+
const url = src.indexOf('://') !== -1 ? src : `file://${src}`;
|
|
382
|
+
return new File(url);
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
exports.listFiles = listFiles;
|
|
386
|
+
class File {
|
|
387
|
+
constructor(path, options = {}) {
|
|
388
|
+
//todo: clean this rats nest up
|
|
389
|
+
const location = (path && path[0] === '/' ? `file:${path}` : path) || !path && options.directory && handleCanonicalPath(options.directory, File.os, File.user) || '/tmp/' + Math.floor(Math.random() * 10000);
|
|
390
|
+
if (options.cache === true) options.cache = internalCache;
|
|
391
|
+
this.options = options;
|
|
392
|
+
//one of: desktop, documents, downloads, music, pictures, videos
|
|
393
|
+
this.directory = options.directory || '.';
|
|
394
|
+
this.path = location;
|
|
395
|
+
this.buffer = new _buffer.FileBuffer();
|
|
396
|
+
}
|
|
397
|
+
async save() {
|
|
398
|
+
await save(this.path, this.directory, this.buffer, this.options);
|
|
399
|
+
return this;
|
|
400
|
+
}
|
|
401
|
+
async load() {
|
|
402
|
+
const dir = this.path.indexOf('/') === -1 ? this.directory : '';
|
|
403
|
+
this.buffer = await load(this.path, dir, this.options);
|
|
404
|
+
this.buffer.cast = type => {
|
|
405
|
+
return _buffer.FileBuffer.to(type, this.buffer);
|
|
406
|
+
};
|
|
407
|
+
return this;
|
|
408
|
+
}
|
|
409
|
+
body(value) {
|
|
410
|
+
if (value === null || value === undefined) return this.buffer;
|
|
411
|
+
this.buffer = _buffer.FileBuffer.from(value);
|
|
412
|
+
this.buffer.cast = type => {
|
|
413
|
+
return _buffer.FileBuffer.to(type, this.buffer);
|
|
414
|
+
};
|
|
415
|
+
if (value) return this;
|
|
416
|
+
return this.buffer;
|
|
417
|
+
}
|
|
418
|
+
async info() {
|
|
419
|
+
return await info(this.path, this.directory);
|
|
420
|
+
}
|
|
421
|
+
async 'delete'() {
|
|
422
|
+
await remove(this.path, this.directory, this.options);
|
|
423
|
+
return this;
|
|
424
|
+
}
|
|
425
|
+
static exists(path, directory) {
|
|
426
|
+
return exists(path, directory);
|
|
427
|
+
}
|
|
428
|
+
static list(path, options) {
|
|
429
|
+
return list(path, options);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
exports.File = File;
|
|
433
|
+
let user = '';
|
|
434
|
+
Object.defineProperty(File, 'user', {
|
|
435
|
+
get() {
|
|
436
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
437
|
+
return user || 'khrome'; //todo: something real;
|
|
438
|
+
} else {
|
|
439
|
+
return user || 'khrome'; //todo: something real;
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
set(newValue) {
|
|
444
|
+
user = newValue;
|
|
445
|
+
},
|
|
446
|
+
enumerable: true,
|
|
447
|
+
configurable: true
|
|
448
|
+
});
|
|
449
|
+
Object.defineProperty(File, 'os', {
|
|
450
|
+
get() {
|
|
451
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
452
|
+
return 'darwin'; //todo: something real;
|
|
453
|
+
} else {
|
|
454
|
+
return 'darwin';
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
set(newValue) {
|
|
458
|
+
//do nothing
|
|
459
|
+
},
|
|
460
|
+
enumerable: true,
|
|
461
|
+
configurable: true
|
|
462
|
+
});
|
|
463
|
+
const canonicalLocationToPath = {
|
|
464
|
+
darwin: {
|
|
465
|
+
'desktop': '~/Desktop',
|
|
466
|
+
'documents': '~/Documents',
|
|
467
|
+
'downloads': '~/Downloads',
|
|
468
|
+
'music': '~/Music',
|
|
469
|
+
'pictures': '~/Pictures',
|
|
470
|
+
'home': '~/Pictures',
|
|
471
|
+
'videos': '~/Movies'
|
|
472
|
+
},
|
|
473
|
+
win: {},
|
|
474
|
+
linux: {}
|
|
475
|
+
};
|
|
476
|
+
const osToHome = {
|
|
477
|
+
darwin: '/Users/${user}',
|
|
478
|
+
win: 'C:/',
|
|
479
|
+
linux: '/Users/${user}'
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/*const handlePath = (path, os, username)=>{
|
|
483
|
+
return path.replace('~', osToHome[os].replace('${user}', username));
|
|
484
|
+
};*/
|
|
485
|
+
|
|
486
|
+
const handleCanonicalPath = (name, os, username) => {
|
|
487
|
+
const path = canonicalLocationToPath[os][name];
|
|
488
|
+
return path.replace('~', osToHome[os].replace('${user}', username));
|
|
489
|
+
};
|
|
490
|
+
File.directory = {};
|
|
491
|
+
Object.defineProperty(File.directory, 'current', {
|
|
492
|
+
get() {
|
|
493
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
494
|
+
const base = document.getElementsByTagName('base')[0];
|
|
495
|
+
let basedir = null;
|
|
496
|
+
if (base && (basedir = base.getAttribute('href'))) {
|
|
497
|
+
return basedir;
|
|
498
|
+
} else {
|
|
499
|
+
let path = window.location.pathname;
|
|
500
|
+
path = path.split('/');
|
|
501
|
+
path.pop(); // drop the top one
|
|
502
|
+
return path.join('/');
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
return process.cwd();
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
set(newValue) {
|
|
509
|
+
//do nothing
|
|
510
|
+
},
|
|
511
|
+
enumerable: true,
|
|
512
|
+
configurable: true
|
|
513
|
+
});
|
|
514
|
+
Object.defineProperty(File, 'agent', {
|
|
515
|
+
get() {
|
|
516
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
517
|
+
//var nVer = navigator.appVersion;
|
|
518
|
+
var nAgt = navigator.userAgent;
|
|
519
|
+
var browserName = navigator.appName;
|
|
520
|
+
var fullVersion = '' + parseFloat(navigator.appVersion);
|
|
521
|
+
var majorVersion = parseInt(navigator.appVersion, 10);
|
|
522
|
+
var nameOffset, verOffset, ix;
|
|
523
|
+
|
|
524
|
+
// In Opera, the true version is after "Opera" or after "Version"
|
|
525
|
+
if ((verOffset = nAgt.indexOf('Opera')) != -1) {
|
|
526
|
+
browserName = 'Opera';
|
|
527
|
+
fullVersion = nAgt.substring(verOffset + 6);
|
|
528
|
+
if ((verOffset = nAgt.indexOf('Version')) != -1) fullVersion = nAgt.substring(verOffset + 8);
|
|
529
|
+
}
|
|
530
|
+
// In MSIE, the true version is after 'MSIE' in userAgent
|
|
531
|
+
else if ((verOffset = nAgt.indexOf('MSIE')) != -1) {
|
|
532
|
+
browserName = 'Microsoft Internet Explorer';
|
|
533
|
+
fullVersion = nAgt.substring(verOffset + 5);
|
|
534
|
+
}
|
|
535
|
+
// In Chrome, the true version is after 'Chrome'
|
|
536
|
+
else if ((verOffset = nAgt.indexOf('Chrome')) != -1) {
|
|
537
|
+
browserName = 'Chrome';
|
|
538
|
+
fullVersion = nAgt.substring(verOffset + 7);
|
|
539
|
+
}
|
|
540
|
+
// In Safari, the true version is after 'Safari' or after 'Version'
|
|
541
|
+
else if ((verOffset = nAgt.indexOf('Safari')) != -1) {
|
|
542
|
+
browserName = 'Safari';
|
|
543
|
+
fullVersion = nAgt.substring(verOffset + 7);
|
|
544
|
+
if ((verOffset = nAgt.indexOf('Version')) != -1) fullVersion = nAgt.substring(verOffset + 8);
|
|
545
|
+
}
|
|
546
|
+
// In Firefox, the true version is after 'Firefox'
|
|
547
|
+
else if ((verOffset = nAgt.indexOf('Firefox')) != -1) {
|
|
548
|
+
browserName = 'Firefox';
|
|
549
|
+
fullVersion = nAgt.substring(verOffset + 8);
|
|
550
|
+
}
|
|
551
|
+
// In most other browsers, 'name/version' is at the end of userAgent
|
|
552
|
+
else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {
|
|
553
|
+
browserName = nAgt.substring(nameOffset, verOffset);
|
|
554
|
+
fullVersion = nAgt.substring(verOffset + 1);
|
|
555
|
+
if (browserName.toLowerCase() == browserName.toUpperCase()) {
|
|
556
|
+
browserName = navigator.appName;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
// trim the fullVersion string at semicolon/space if present
|
|
560
|
+
if ((ix = fullVersion.indexOf(';')) != -1) fullVersion = fullVersion.substring(0, ix);
|
|
561
|
+
if ((ix = fullVersion.indexOf(' ')) != -1) fullVersion = fullVersion.substring(0, ix);
|
|
562
|
+
majorVersion = parseInt('' + fullVersion, 10);
|
|
563
|
+
if (isNaN(majorVersion)) {
|
|
564
|
+
fullVersion = '' + parseFloat(navigator.appVersion);
|
|
565
|
+
majorVersion = parseInt(navigator.appVersion, 10);
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
name: browserName.toLowerCase(),
|
|
569
|
+
version: fullVersion,
|
|
570
|
+
major: majorVersion
|
|
571
|
+
};
|
|
572
|
+
} else {
|
|
573
|
+
return {};
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
set(newValue) {
|
|
577
|
+
//do nothing
|
|
578
|
+
},
|
|
579
|
+
enumerable: true,
|
|
580
|
+
configurable: true
|
|
581
|
+
});
|
|
582
|
+
const directoryGet = type => {
|
|
583
|
+
if (_browserOrNode.isBrowser || _browserOrNode.isJsDom) {
|
|
584
|
+
return handleCanonicalPath('home', File.os, File.user);
|
|
585
|
+
} else {
|
|
586
|
+
return process.cwd();
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
Object.keys(canonicalLocationToPath['darwin']).forEach(key => {
|
|
590
|
+
// register all available keys
|
|
591
|
+
Object.defineProperty(File.directory, key, {
|
|
592
|
+
enumerable: true,
|
|
593
|
+
configurable: true,
|
|
594
|
+
get() {
|
|
595
|
+
return directoryGet(key);
|
|
596
|
+
},
|
|
597
|
+
set(newValue) {}
|
|
598
|
+
});
|
|
599
|
+
});
|
package/index.html
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<link rel="icon" href="/favicon.ico">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Loading...</title>
|
|
8
|
+
<style>
|
|
9
|
+
body{
|
|
10
|
+
padding: 0px;
|
|
11
|
+
margin: 0px;
|
|
12
|
+
width:100%;
|
|
13
|
+
height: 100%;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
16
|
+
#forkme{
|
|
17
|
+
position: absolute;
|
|
18
|
+
top: 0px;
|
|
19
|
+
right: 0px;
|
|
20
|
+
display: block;
|
|
21
|
+
}
|
|
22
|
+
</style>
|
|
23
|
+
<script type="importmap">{ "imports" :{
|
|
24
|
+
"browser-or-node":"https://unpkg.com/browser-or-node@2.1.1/src/index.js",
|
|
25
|
+
"@environment-safe/package" : "https://unpkg.com/@environment-safe/package@0.1.3/src/index.mjs",
|
|
26
|
+
"module": "https://unpkg.com/browser-or-node@2.1.1/src/index.js",
|
|
27
|
+
"node:events": "https://unpkg.com/extended-emitter@1.3.2/node-events.mjs",
|
|
28
|
+
"path": "https://unpkg.com/browser-or-node@2.1.1/src/index.js"
|
|
29
|
+
}}</script>
|
|
30
|
+
<script
|
|
31
|
+
type="module"
|
|
32
|
+
src="https://cdn.jsdelivr.net/gh/zerodevx/zero-md@2/dist/zero-md.min.js"
|
|
33
|
+
></script>
|
|
34
|
+
<script type="module">
|
|
35
|
+
import { getPackage } from '@environment-safe/package';
|
|
36
|
+
const pkgReady = getPackage();
|
|
37
|
+
const ready = Promise.all([
|
|
38
|
+
pkgReady,
|
|
39
|
+
new Promise((resolve)=>{
|
|
40
|
+
window.pkgDone = resolve;
|
|
41
|
+
})
|
|
42
|
+
]);
|
|
43
|
+
const getUrl = (repository)=>{
|
|
44
|
+
if(repository.url && repository.url.indexOf('://') !==-1) return repository.url;
|
|
45
|
+
if(repository.url){
|
|
46
|
+
if(repository.url.indexOf('git@') === 0){
|
|
47
|
+
const parts = repository.url.substring(4).split(':');
|
|
48
|
+
if(parts[1].indexOf('.git') !== -1){
|
|
49
|
+
parts[1] = parts[1].substring(0, parts[1].length-4);
|
|
50
|
+
}
|
|
51
|
+
switch(parts[0]){
|
|
52
|
+
case 'github.com' : return `https://github.com/${parts[1]}`;
|
|
53
|
+
default: throw new Error(`Unknown source`);
|
|
54
|
+
}
|
|
55
|
+
console.log(parts);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const pkg = await pkgReady;
|
|
60
|
+
window.pkg = pkg;
|
|
61
|
+
console.log(pkg);
|
|
62
|
+
await ready;
|
|
63
|
+
document.title = pkg.name;
|
|
64
|
+
document.write(`<a style="position: absolute; top: 0px; right: 0px; display: block;" id="forkme" href="${getUrl(pkg.repository)}" class="ribbon"><img decoding="async" loading="lazy" width="149" height="149" src="https://github.blog/wp-content/uploads/2008/12/forkme_right_red_aa0000.png?resize=149%2C149" alt="Fork me on GitHub" data-recalc-dims="1"></a>`)
|
|
65
|
+
document.write(`<h1>${pkg.name}@${pkg.version}</h1>`);
|
|
66
|
+
document.write(`<table><tr>`);
|
|
67
|
+
document.write(`<td><a href="docs/">Documentation</a><a href="test/">Tests</a></td>`);
|
|
68
|
+
document.write(`</tr></table>`);
|
|
69
|
+
document.write(`<p>${pkg.description}</p>`);
|
|
70
|
+
document.write(`<zero-md src="README.md"></zero-md>`)
|
|
71
|
+
</script>
|
|
72
|
+
</head>
|
|
73
|
+
<body onload="window.pkgDone()"></body>
|
|
74
|
+
</html>
|