@jsenv/core 38.3.7 → 38.3.8
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/dist/jsenv_core.js +615 -599
- package/package.json +8 -8
package/dist/jsenv_core.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { chmod, stat, lstat,
|
|
1
|
+
import { readdir, chmod, stat, lstat, promises, writeFileSync as writeFileSync$1, mkdirSync, unlink, openSync, closeSync, rmdir, watch, readdirSync, statSync, createReadStream, readFile, existsSync, readFileSync, realpathSync } from "node:fs";
|
|
2
2
|
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
3
|
-
import crypto, { createHash } from "node:crypto";
|
|
4
3
|
import { extname } from "node:path";
|
|
4
|
+
import crypto, { createHash } from "node:crypto";
|
|
5
5
|
import process$1 from "node:process";
|
|
6
6
|
import os, { networkInterfaces } from "node:os";
|
|
7
7
|
import tty from "node:tty";
|
|
@@ -61,6 +61,7 @@ const isWindowsPathnameSpecifier = (specifier) => {
|
|
|
61
61
|
const hasScheme$1 = (specifier) => /^[a-zA-Z]+:/.test(specifier);
|
|
62
62
|
|
|
63
63
|
const resolveAssociations = (associations, baseUrl) => {
|
|
64
|
+
if (baseUrl && typeof baseUrl.href === "string") baseUrl = baseUrl.href;
|
|
64
65
|
assertUrlLike(baseUrl, "baseUrl");
|
|
65
66
|
const associationsResolved = {};
|
|
66
67
|
Object.keys(associations).forEach((key) => {
|
|
@@ -144,6 +145,7 @@ const asFlatAssociations = (associations) => {
|
|
|
144
145
|
*/
|
|
145
146
|
const applyPatternMatching = ({ url, pattern }) => {
|
|
146
147
|
assertUrlLike(pattern, "pattern");
|
|
148
|
+
if (url && typeof url.href === "string") url = url.href;
|
|
147
149
|
assertUrlLike(url, "url");
|
|
148
150
|
const { matched, patternIndex, index, groups } = applyMatching(pattern, url);
|
|
149
151
|
const matchGroups = [];
|
|
@@ -391,6 +393,7 @@ const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
|
|
|
391
393
|
};
|
|
392
394
|
|
|
393
395
|
const applyAssociations = ({ url, associations }) => {
|
|
396
|
+
if (url && typeof url.href === "string") url = url.href;
|
|
394
397
|
assertUrlLike(url);
|
|
395
398
|
const flatAssociations = asFlatAssociations(associations);
|
|
396
399
|
return Object.keys(flatAssociations).reduce((previousValue, pattern) => {
|
|
@@ -440,6 +443,7 @@ const applyAliases = ({ url, aliases }) => {
|
|
|
440
443
|
};
|
|
441
444
|
|
|
442
445
|
const urlChildMayMatch = ({ url, associations, predicate }) => {
|
|
446
|
+
if (url && typeof url.href === "string") url = url.href;
|
|
443
447
|
assertUrlLike(url, "url");
|
|
444
448
|
// the function was meants to be used on url ending with '/'
|
|
445
449
|
if (!url.endsWith("/")) {
|
|
@@ -1273,291 +1277,163 @@ const assertAndNormalizeFileUrl = (
|
|
|
1273
1277
|
return value;
|
|
1274
1278
|
};
|
|
1275
1279
|
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
if (stats.isSymbolicLink()) return "symbolic-link";
|
|
1280
|
-
if (stats.isFIFO()) return "fifo";
|
|
1281
|
-
if (stats.isSocket()) return "socket";
|
|
1282
|
-
if (stats.isCharacterDevice()) return "character-device";
|
|
1283
|
-
if (stats.isBlockDevice()) return "block-device";
|
|
1284
|
-
return undefined;
|
|
1285
|
-
};
|
|
1286
|
-
|
|
1287
|
-
// https://github.com/coderaiser/cloudcmd/issues/63#issuecomment-195478143
|
|
1288
|
-
// https://nodejs.org/api/fs.html#fs_file_modes
|
|
1289
|
-
// https://github.com/TooTallNate/stat-mode
|
|
1280
|
+
const comparePathnames = (leftPathame, rightPathname) => {
|
|
1281
|
+
const leftPartArray = leftPathame.split("/");
|
|
1282
|
+
const rightPartArray = rightPathname.split("/");
|
|
1290
1283
|
|
|
1291
|
-
|
|
1292
|
-
const
|
|
1293
|
-
const S_IWUSR = 128; /* 0000200 write permission, owner */
|
|
1294
|
-
const S_IXUSR = 64; /* 0000100 execute/search permission, owner */
|
|
1295
|
-
const S_IRGRP = 32; /* 0000040 read permission, group */
|
|
1296
|
-
const S_IWGRP = 16; /* 0000020 write permission, group */
|
|
1297
|
-
const S_IXGRP = 8; /* 0000010 execute/search permission, group */
|
|
1298
|
-
const S_IROTH = 4; /* 0000004 read permission, others */
|
|
1299
|
-
const S_IWOTH = 2; /* 0000002 write permission, others */
|
|
1300
|
-
const S_IXOTH = 1; /* 0000001 execute/search permission, others */
|
|
1284
|
+
const leftLength = leftPartArray.length;
|
|
1285
|
+
const rightLength = rightPartArray.length;
|
|
1301
1286
|
|
|
1302
|
-
const
|
|
1303
|
-
let
|
|
1287
|
+
const maxLength = Math.max(leftLength, rightLength);
|
|
1288
|
+
let i = 0;
|
|
1289
|
+
while (i < maxLength) {
|
|
1290
|
+
const leftPartExists = i in leftPartArray;
|
|
1291
|
+
const rightPartExists = i in rightPartArray;
|
|
1304
1292
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1293
|
+
// longer comes first
|
|
1294
|
+
if (!leftPartExists) {
|
|
1295
|
+
return +1;
|
|
1296
|
+
}
|
|
1297
|
+
if (!rightPartExists) {
|
|
1298
|
+
return -1;
|
|
1299
|
+
}
|
|
1308
1300
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1301
|
+
const leftPartIsLast = i === leftPartArray.length - 1;
|
|
1302
|
+
const rightPartIsLast = i === rightPartArray.length - 1;
|
|
1303
|
+
// folder comes first
|
|
1304
|
+
if (leftPartIsLast && !rightPartIsLast) {
|
|
1305
|
+
return +1;
|
|
1306
|
+
}
|
|
1307
|
+
if (!leftPartIsLast && rightPartIsLast) {
|
|
1308
|
+
return -1;
|
|
1309
|
+
}
|
|
1312
1310
|
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1311
|
+
const leftPart = leftPartArray[i];
|
|
1312
|
+
const rightPart = rightPartArray[i];
|
|
1313
|
+
i++;
|
|
1314
|
+
// local comparison comes first
|
|
1315
|
+
const comparison = leftPart.localeCompare(rightPart);
|
|
1316
|
+
if (comparison !== 0) {
|
|
1317
|
+
return comparison;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1316
1320
|
|
|
1317
|
-
|
|
1321
|
+
if (leftLength < rightLength) {
|
|
1322
|
+
return +1;
|
|
1323
|
+
}
|
|
1324
|
+
if (leftLength > rightLength) {
|
|
1325
|
+
return -1;
|
|
1326
|
+
}
|
|
1327
|
+
return 0;
|
|
1318
1328
|
};
|
|
1319
1329
|
|
|
1320
|
-
const
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
let binaryFlags;
|
|
1324
|
-
if (typeof permissions === "object") {
|
|
1325
|
-
permissions = {
|
|
1326
|
-
owner: {
|
|
1327
|
-
read: getPermissionOrComputeDefault("read", "owner", permissions),
|
|
1328
|
-
write: getPermissionOrComputeDefault("write", "owner", permissions),
|
|
1329
|
-
execute: getPermissionOrComputeDefault("execute", "owner", permissions),
|
|
1330
|
-
},
|
|
1331
|
-
group: {
|
|
1332
|
-
read: getPermissionOrComputeDefault("read", "group", permissions),
|
|
1333
|
-
write: getPermissionOrComputeDefault("write", "group", permissions),
|
|
1334
|
-
execute: getPermissionOrComputeDefault("execute", "group", permissions),
|
|
1335
|
-
},
|
|
1336
|
-
others: {
|
|
1337
|
-
read: getPermissionOrComputeDefault("read", "others", permissions),
|
|
1338
|
-
write: getPermissionOrComputeDefault("write", "others", permissions),
|
|
1339
|
-
execute: getPermissionOrComputeDefault(
|
|
1340
|
-
"execute",
|
|
1341
|
-
"others",
|
|
1342
|
-
permissions,
|
|
1343
|
-
),
|
|
1344
|
-
},
|
|
1345
|
-
};
|
|
1346
|
-
binaryFlags = permissionsToBinaryFlags(permissions);
|
|
1347
|
-
} else {
|
|
1348
|
-
binaryFlags = permissions;
|
|
1349
|
-
}
|
|
1330
|
+
const isWindows$3 = process.platform === "win32";
|
|
1331
|
+
const baseUrlFallback = fileSystemPathToUrl$1(process.cwd());
|
|
1350
1332
|
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1333
|
+
/**
|
|
1334
|
+
* Some url might be resolved or remapped to url without the windows drive letter.
|
|
1335
|
+
* For instance
|
|
1336
|
+
* new URL('/foo.js', 'file:///C:/dir/file.js')
|
|
1337
|
+
* resolves to
|
|
1338
|
+
* 'file:///foo.js'
|
|
1339
|
+
*
|
|
1340
|
+
* But on windows it becomes a problem because we need the drive letter otherwise
|
|
1341
|
+
* url cannot be converted to a filesystem path.
|
|
1342
|
+
*
|
|
1343
|
+
* ensureWindowsDriveLetter ensure a resolved url still contains the drive letter.
|
|
1344
|
+
*/
|
|
1361
1345
|
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1346
|
+
const ensureWindowsDriveLetter = (url, baseUrl) => {
|
|
1347
|
+
try {
|
|
1348
|
+
url = String(new URL(url));
|
|
1349
|
+
} catch (e) {
|
|
1350
|
+
throw new Error(`absolute url expected but got ${url}`);
|
|
1351
|
+
}
|
|
1364
1352
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
if (action in subjectPermissions) {
|
|
1369
|
-
return subjectPermissions[action];
|
|
1370
|
-
}
|
|
1353
|
+
if (!isWindows$3) {
|
|
1354
|
+
return url;
|
|
1355
|
+
}
|
|
1371
1356
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1357
|
+
try {
|
|
1358
|
+
baseUrl = String(new URL(baseUrl));
|
|
1359
|
+
} catch (e) {
|
|
1360
|
+
throw new Error(
|
|
1361
|
+
`absolute baseUrl expected but got ${baseUrl} to ensure windows drive letter on ${url}`,
|
|
1377
1362
|
);
|
|
1378
|
-
if (actionFallback) {
|
|
1379
|
-
return subjectPermissions[actionFallback];
|
|
1380
|
-
}
|
|
1381
1363
|
}
|
|
1382
1364
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1365
|
+
if (!url.startsWith("file://")) {
|
|
1366
|
+
return url;
|
|
1367
|
+
}
|
|
1368
|
+
const afterProtocol = url.slice("file://".length);
|
|
1369
|
+
// we still have the windows drive letter
|
|
1370
|
+
if (extractDriveLetter(afterProtocol)) {
|
|
1371
|
+
return url;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// drive letter was lost, restore it
|
|
1375
|
+
const baseUrlOrFallback = baseUrl.startsWith("file://")
|
|
1376
|
+
? baseUrl
|
|
1377
|
+
: baseUrlFallback;
|
|
1378
|
+
const driveLetter = extractDriveLetter(
|
|
1379
|
+
baseUrlOrFallback.slice("file://".length),
|
|
1390
1380
|
);
|
|
1391
|
-
if (
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
: getPermissionOrComputeDefault(action, subjectFallback, permissions);
|
|
1381
|
+
if (!driveLetter) {
|
|
1382
|
+
throw new Error(
|
|
1383
|
+
`drive letter expected on baseUrl but got ${baseUrl} to ensure windows drive letter on ${url}`,
|
|
1384
|
+
);
|
|
1396
1385
|
}
|
|
1386
|
+
return `file:///${driveLetter}:${afterProtocol}`;
|
|
1387
|
+
};
|
|
1397
1388
|
|
|
1398
|
-
|
|
1389
|
+
const extractDriveLetter = (resource) => {
|
|
1390
|
+
// we still have the windows drive letter
|
|
1391
|
+
if (/[a-zA-Z]/.test(resource[1]) && resource[2] === ":") {
|
|
1392
|
+
return resource[1];
|
|
1393
|
+
}
|
|
1394
|
+
return null;
|
|
1399
1395
|
};
|
|
1400
1396
|
|
|
1401
1397
|
/*
|
|
1402
|
-
*
|
|
1403
|
-
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
|
|
1398
|
+
* See callback_race.md
|
|
1404
1399
|
*/
|
|
1405
1400
|
|
|
1401
|
+
const raceCallbacks = (raceDescription, winnerCallback) => {
|
|
1402
|
+
let cleanCallbacks = [];
|
|
1403
|
+
let status = "racing";
|
|
1406
1404
|
|
|
1407
|
-
const
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
let sourceUrl = assertAndNormalizeFileUrl(source);
|
|
1414
|
-
if (sourceUrl.endsWith("/")) sourceUrl = sourceUrl.slice(0, -1);
|
|
1405
|
+
const clean = () => {
|
|
1406
|
+
cleanCallbacks.forEach((clean) => {
|
|
1407
|
+
clean();
|
|
1408
|
+
});
|
|
1409
|
+
cleanCallbacks = null;
|
|
1410
|
+
};
|
|
1415
1411
|
|
|
1416
|
-
const
|
|
1412
|
+
const cancel = () => {
|
|
1413
|
+
if (status !== "racing") {
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
status = "cancelled";
|
|
1417
|
+
clean();
|
|
1418
|
+
};
|
|
1417
1419
|
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1420
|
+
Object.keys(raceDescription).forEach((candidateName) => {
|
|
1421
|
+
const register = raceDescription[candidateName];
|
|
1422
|
+
const returnValue = register((data) => {
|
|
1423
|
+
if (status !== "racing") {
|
|
1424
|
+
return;
|
|
1421
1425
|
}
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
);
|
|
1434
|
-
|
|
1435
|
-
try {
|
|
1436
|
-
// unfortunately it means we mutate the permissions
|
|
1437
|
-
// without being able to restore them to the previous value
|
|
1438
|
-
// (because reading current permission would also throw)
|
|
1439
|
-
await writeEntryPermissions(sourceUrl, 0o666);
|
|
1440
|
-
const stats = await readStat(sourcePath, {
|
|
1441
|
-
followLink,
|
|
1442
|
-
...handleNotFoundOption,
|
|
1443
|
-
// could not fix the permission error, give up and throw original error
|
|
1444
|
-
handlePermissionDeniedError: () => {
|
|
1445
|
-
console.error(`still got EPERM after stats on ${sourcePath}`);
|
|
1446
|
-
throw error;
|
|
1447
|
-
},
|
|
1448
|
-
});
|
|
1449
|
-
return stats;
|
|
1450
|
-
} catch (e) {
|
|
1451
|
-
console.error(
|
|
1452
|
-
`error while trying to fix windows EPERM after stats on ${sourcePath}: ${e.stack}`,
|
|
1453
|
-
);
|
|
1454
|
-
throw error;
|
|
1455
|
-
}
|
|
1456
|
-
},
|
|
1457
|
-
}
|
|
1458
|
-
: {}),
|
|
1459
|
-
});
|
|
1460
|
-
};
|
|
1461
|
-
|
|
1462
|
-
const readStat = (
|
|
1463
|
-
sourcePath,
|
|
1464
|
-
{
|
|
1465
|
-
followLink,
|
|
1466
|
-
handleNotFoundError = null,
|
|
1467
|
-
handlePermissionDeniedError = null,
|
|
1468
|
-
} = {},
|
|
1469
|
-
) => {
|
|
1470
|
-
const nodeMethod = followLink ? stat : lstat;
|
|
1471
|
-
|
|
1472
|
-
return new Promise((resolve, reject) => {
|
|
1473
|
-
nodeMethod(sourcePath, (error, statsObject) => {
|
|
1474
|
-
if (error) {
|
|
1475
|
-
if (handleNotFoundError && error.code === "ENOENT") {
|
|
1476
|
-
resolve(handleNotFoundError(error));
|
|
1477
|
-
} else if (
|
|
1478
|
-
handlePermissionDeniedError &&
|
|
1479
|
-
(error.code === "EPERM" || error.code === "EACCES")
|
|
1480
|
-
) {
|
|
1481
|
-
resolve(handlePermissionDeniedError(error));
|
|
1482
|
-
} else {
|
|
1483
|
-
reject(error);
|
|
1484
|
-
}
|
|
1485
|
-
} else {
|
|
1486
|
-
resolve(statsObject);
|
|
1487
|
-
}
|
|
1488
|
-
});
|
|
1489
|
-
});
|
|
1490
|
-
};
|
|
1491
|
-
|
|
1492
|
-
/*
|
|
1493
|
-
* - Buffer documentation on Node.js
|
|
1494
|
-
* https://nodejs.org/docs/latest-v13.x/api/buffer.html
|
|
1495
|
-
* - eTag documentation on MDN
|
|
1496
|
-
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
|
|
1497
|
-
*/
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
const ETAG_FOR_EMPTY_CONTENT$1 = '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
|
|
1501
|
-
|
|
1502
|
-
const bufferToEtag$1 = (buffer) => {
|
|
1503
|
-
if (!Buffer.isBuffer(buffer)) {
|
|
1504
|
-
throw new TypeError(`buffer expected, got ${buffer}`);
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
if (buffer.length === 0) {
|
|
1508
|
-
return ETAG_FOR_EMPTY_CONTENT$1;
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
const hash = createHash("sha1");
|
|
1512
|
-
hash.update(buffer, "utf8");
|
|
1513
|
-
|
|
1514
|
-
const hashBase64String = hash.digest("base64");
|
|
1515
|
-
const hashBase64StringSubset = hashBase64String.slice(0, 27);
|
|
1516
|
-
const length = buffer.length;
|
|
1517
|
-
|
|
1518
|
-
return `"${length.toString(16)}-${hashBase64StringSubset}"`;
|
|
1519
|
-
};
|
|
1520
|
-
|
|
1521
|
-
/*
|
|
1522
|
-
* See callback_race.md
|
|
1523
|
-
*/
|
|
1524
|
-
|
|
1525
|
-
const raceCallbacks = (raceDescription, winnerCallback) => {
|
|
1526
|
-
let cleanCallbacks = [];
|
|
1527
|
-
let status = "racing";
|
|
1528
|
-
|
|
1529
|
-
const clean = () => {
|
|
1530
|
-
cleanCallbacks.forEach((clean) => {
|
|
1531
|
-
clean();
|
|
1532
|
-
});
|
|
1533
|
-
cleanCallbacks = null;
|
|
1534
|
-
};
|
|
1535
|
-
|
|
1536
|
-
const cancel = () => {
|
|
1537
|
-
if (status !== "racing") {
|
|
1538
|
-
return;
|
|
1539
|
-
}
|
|
1540
|
-
status = "cancelled";
|
|
1541
|
-
clean();
|
|
1542
|
-
};
|
|
1543
|
-
|
|
1544
|
-
Object.keys(raceDescription).forEach((candidateName) => {
|
|
1545
|
-
const register = raceDescription[candidateName];
|
|
1546
|
-
const returnValue = register((data) => {
|
|
1547
|
-
if (status !== "racing") {
|
|
1548
|
-
return;
|
|
1549
|
-
}
|
|
1550
|
-
status = "done";
|
|
1551
|
-
clean();
|
|
1552
|
-
winnerCallback({
|
|
1553
|
-
name: candidateName,
|
|
1554
|
-
data,
|
|
1555
|
-
});
|
|
1556
|
-
});
|
|
1557
|
-
if (typeof returnValue === "function") {
|
|
1558
|
-
cleanCallbacks.push(returnValue);
|
|
1559
|
-
}
|
|
1560
|
-
});
|
|
1426
|
+
status = "done";
|
|
1427
|
+
clean();
|
|
1428
|
+
winnerCallback({
|
|
1429
|
+
name: candidateName,
|
|
1430
|
+
data,
|
|
1431
|
+
});
|
|
1432
|
+
});
|
|
1433
|
+
if (typeof returnValue === "function") {
|
|
1434
|
+
cleanCallbacks.push(returnValue);
|
|
1435
|
+
}
|
|
1436
|
+
});
|
|
1561
1437
|
|
|
1562
1438
|
return cancel;
|
|
1563
1439
|
};
|
|
@@ -2044,78 +1920,244 @@ const readDirectory = async (url, { emfileMaxWait = 1000 } = {}) => {
|
|
|
2044
1920
|
return attempt();
|
|
2045
1921
|
};
|
|
2046
1922
|
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
1923
|
+
// https://github.com/coderaiser/cloudcmd/issues/63#issuecomment-195478143
|
|
1924
|
+
// https://nodejs.org/api/fs.html#fs_file_modes
|
|
1925
|
+
// https://github.com/TooTallNate/stat-mode
|
|
2050
1926
|
|
|
2051
|
-
|
|
2052
|
-
|
|
1927
|
+
// cannot get from fs.constants because they are not available on windows
|
|
1928
|
+
const S_IRUSR = 256; /* 0000400 read permission, owner */
|
|
1929
|
+
const S_IWUSR = 128; /* 0000200 write permission, owner */
|
|
1930
|
+
const S_IXUSR = 64; /* 0000100 execute/search permission, owner */
|
|
1931
|
+
const S_IRGRP = 32; /* 0000040 read permission, group */
|
|
1932
|
+
const S_IWGRP = 16; /* 0000020 write permission, group */
|
|
1933
|
+
const S_IXGRP = 8; /* 0000010 execute/search permission, group */
|
|
1934
|
+
const S_IROTH = 4; /* 0000004 read permission, others */
|
|
1935
|
+
const S_IWOTH = 2; /* 0000002 write permission, others */
|
|
1936
|
+
const S_IXOTH = 1; /* 0000001 execute/search permission, others */
|
|
2053
1937
|
|
|
2054
|
-
|
|
2055
|
-
let
|
|
2056
|
-
while (i < maxLength) {
|
|
2057
|
-
const leftPartExists = i in leftPartArray;
|
|
2058
|
-
const rightPartExists = i in rightPartArray;
|
|
1938
|
+
const permissionsToBinaryFlags = ({ owner, group, others }) => {
|
|
1939
|
+
let binaryFlags = 0;
|
|
2059
1940
|
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
}
|
|
2064
|
-
if (!rightPartExists) {
|
|
2065
|
-
return -1;
|
|
2066
|
-
}
|
|
1941
|
+
if (owner.read) binaryFlags |= S_IRUSR;
|
|
1942
|
+
if (owner.write) binaryFlags |= S_IWUSR;
|
|
1943
|
+
if (owner.execute) binaryFlags |= S_IXUSR;
|
|
2067
1944
|
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
if (leftPartIsLast && !rightPartIsLast) {
|
|
2072
|
-
return +1;
|
|
2073
|
-
}
|
|
2074
|
-
if (!leftPartIsLast && rightPartIsLast) {
|
|
2075
|
-
return -1;
|
|
2076
|
-
}
|
|
1945
|
+
if (group.read) binaryFlags |= S_IRGRP;
|
|
1946
|
+
if (group.write) binaryFlags |= S_IWGRP;
|
|
1947
|
+
if (group.execute) binaryFlags |= S_IXGRP;
|
|
2077
1948
|
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
// local comparison comes first
|
|
2082
|
-
const comparison = leftPart.localeCompare(rightPart);
|
|
2083
|
-
if (comparison !== 0) {
|
|
2084
|
-
return comparison;
|
|
2085
|
-
}
|
|
2086
|
-
}
|
|
1949
|
+
if (others.read) binaryFlags |= S_IROTH;
|
|
1950
|
+
if (others.write) binaryFlags |= S_IWOTH;
|
|
1951
|
+
if (others.execute) binaryFlags |= S_IXOTH;
|
|
2087
1952
|
|
|
2088
|
-
|
|
2089
|
-
return +1;
|
|
2090
|
-
}
|
|
2091
|
-
if (leftLength > rightLength) {
|
|
2092
|
-
return -1;
|
|
2093
|
-
}
|
|
2094
|
-
return 0;
|
|
1953
|
+
return binaryFlags;
|
|
2095
1954
|
};
|
|
2096
1955
|
|
|
2097
|
-
|
|
2098
|
-
const
|
|
2099
|
-
|
|
2100
|
-
const writeDirectory = async (
|
|
2101
|
-
destination,
|
|
2102
|
-
{ recursive = true, allowUseless = false } = {},
|
|
2103
|
-
) => {
|
|
2104
|
-
const destinationUrl = assertAndNormalizeDirectoryUrl(destination);
|
|
2105
|
-
const destinationPath = urlToFileSystemPath(destinationUrl);
|
|
1956
|
+
const writeEntryPermissions = async (source, permissions) => {
|
|
1957
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
2106
1958
|
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
1959
|
+
let binaryFlags;
|
|
1960
|
+
if (typeof permissions === "object") {
|
|
1961
|
+
permissions = {
|
|
1962
|
+
owner: {
|
|
1963
|
+
read: getPermissionOrComputeDefault("read", "owner", permissions),
|
|
1964
|
+
write: getPermissionOrComputeDefault("write", "owner", permissions),
|
|
1965
|
+
execute: getPermissionOrComputeDefault("execute", "owner", permissions),
|
|
1966
|
+
},
|
|
1967
|
+
group: {
|
|
1968
|
+
read: getPermissionOrComputeDefault("read", "group", permissions),
|
|
1969
|
+
write: getPermissionOrComputeDefault("write", "group", permissions),
|
|
1970
|
+
execute: getPermissionOrComputeDefault("execute", "group", permissions),
|
|
1971
|
+
},
|
|
1972
|
+
others: {
|
|
1973
|
+
read: getPermissionOrComputeDefault("read", "others", permissions),
|
|
1974
|
+
write: getPermissionOrComputeDefault("write", "others", permissions),
|
|
1975
|
+
execute: getPermissionOrComputeDefault(
|
|
1976
|
+
"execute",
|
|
1977
|
+
"others",
|
|
1978
|
+
permissions,
|
|
1979
|
+
),
|
|
1980
|
+
},
|
|
1981
|
+
};
|
|
1982
|
+
binaryFlags = permissionsToBinaryFlags(permissions);
|
|
1983
|
+
} else {
|
|
1984
|
+
binaryFlags = permissions;
|
|
1985
|
+
}
|
|
2111
1986
|
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
if (
|
|
2115
|
-
|
|
1987
|
+
return new Promise((resolve, reject) => {
|
|
1988
|
+
chmod(new URL(sourceUrl), binaryFlags, (error) => {
|
|
1989
|
+
if (error) {
|
|
1990
|
+
reject(error);
|
|
1991
|
+
} else {
|
|
1992
|
+
resolve();
|
|
2116
1993
|
}
|
|
2117
|
-
|
|
2118
|
-
|
|
1994
|
+
});
|
|
1995
|
+
});
|
|
1996
|
+
};
|
|
1997
|
+
|
|
1998
|
+
const actionLevels = { read: 0, write: 1, execute: 2 };
|
|
1999
|
+
const subjectLevels = { others: 0, group: 1, owner: 2 };
|
|
2000
|
+
|
|
2001
|
+
const getPermissionOrComputeDefault = (action, subject, permissions) => {
|
|
2002
|
+
if (subject in permissions) {
|
|
2003
|
+
const subjectPermissions = permissions[subject];
|
|
2004
|
+
if (action in subjectPermissions) {
|
|
2005
|
+
return subjectPermissions[action];
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
const actionLevel = actionLevels[action];
|
|
2009
|
+
const actionFallback = Object.keys(actionLevels).find(
|
|
2010
|
+
(actionFallbackCandidate) =>
|
|
2011
|
+
actionLevels[actionFallbackCandidate] > actionLevel &&
|
|
2012
|
+
actionFallbackCandidate in subjectPermissions,
|
|
2013
|
+
);
|
|
2014
|
+
if (actionFallback) {
|
|
2015
|
+
return subjectPermissions[actionFallback];
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
const subjectLevel = subjectLevels[subject];
|
|
2020
|
+
// do we have a subject with a stronger level (group or owner)
|
|
2021
|
+
// where we could read the action permission ?
|
|
2022
|
+
const subjectFallback = Object.keys(subjectLevels).find(
|
|
2023
|
+
(subjectFallbackCandidate) =>
|
|
2024
|
+
subjectLevels[subjectFallbackCandidate] > subjectLevel &&
|
|
2025
|
+
subjectFallbackCandidate in permissions,
|
|
2026
|
+
);
|
|
2027
|
+
if (subjectFallback) {
|
|
2028
|
+
const subjectPermissions = permissions[subjectFallback];
|
|
2029
|
+
return action in subjectPermissions
|
|
2030
|
+
? subjectPermissions[action]
|
|
2031
|
+
: getPermissionOrComputeDefault(action, subjectFallback, permissions);
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
return false;
|
|
2035
|
+
};
|
|
2036
|
+
|
|
2037
|
+
/*
|
|
2038
|
+
* - stats object documentation on Node.js
|
|
2039
|
+
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
|
|
2040
|
+
*/
|
|
2041
|
+
|
|
2042
|
+
|
|
2043
|
+
const isWindows$2 = process.platform === "win32";
|
|
2044
|
+
|
|
2045
|
+
const readEntryStat = async (
|
|
2046
|
+
source,
|
|
2047
|
+
{ nullIfNotFound = false, followLink = true } = {},
|
|
2048
|
+
) => {
|
|
2049
|
+
let sourceUrl = assertAndNormalizeFileUrl(source);
|
|
2050
|
+
if (sourceUrl.endsWith("/")) sourceUrl = sourceUrl.slice(0, -1);
|
|
2051
|
+
|
|
2052
|
+
const sourcePath = urlToFileSystemPath(sourceUrl);
|
|
2053
|
+
|
|
2054
|
+
const handleNotFoundOption = nullIfNotFound
|
|
2055
|
+
? {
|
|
2056
|
+
handleNotFoundError: () => null,
|
|
2057
|
+
}
|
|
2058
|
+
: {};
|
|
2059
|
+
|
|
2060
|
+
return readStat(sourcePath, {
|
|
2061
|
+
followLink,
|
|
2062
|
+
...handleNotFoundOption,
|
|
2063
|
+
...(isWindows$2
|
|
2064
|
+
? {
|
|
2065
|
+
// Windows can EPERM on stat
|
|
2066
|
+
handlePermissionDeniedError: async (error) => {
|
|
2067
|
+
console.error(
|
|
2068
|
+
`trying to fix windows EPERM after stats on ${sourcePath}`,
|
|
2069
|
+
);
|
|
2070
|
+
|
|
2071
|
+
try {
|
|
2072
|
+
// unfortunately it means we mutate the permissions
|
|
2073
|
+
// without being able to restore them to the previous value
|
|
2074
|
+
// (because reading current permission would also throw)
|
|
2075
|
+
await writeEntryPermissions(sourceUrl, 0o666);
|
|
2076
|
+
const stats = await readStat(sourcePath, {
|
|
2077
|
+
followLink,
|
|
2078
|
+
...handleNotFoundOption,
|
|
2079
|
+
// could not fix the permission error, give up and throw original error
|
|
2080
|
+
handlePermissionDeniedError: () => {
|
|
2081
|
+
console.error(`still got EPERM after stats on ${sourcePath}`);
|
|
2082
|
+
throw error;
|
|
2083
|
+
},
|
|
2084
|
+
});
|
|
2085
|
+
return stats;
|
|
2086
|
+
} catch (e) {
|
|
2087
|
+
console.error(
|
|
2088
|
+
`error while trying to fix windows EPERM after stats on ${sourcePath}: ${e.stack}`,
|
|
2089
|
+
);
|
|
2090
|
+
throw error;
|
|
2091
|
+
}
|
|
2092
|
+
},
|
|
2093
|
+
}
|
|
2094
|
+
: {}),
|
|
2095
|
+
});
|
|
2096
|
+
};
|
|
2097
|
+
|
|
2098
|
+
const readStat = (
|
|
2099
|
+
sourcePath,
|
|
2100
|
+
{
|
|
2101
|
+
followLink,
|
|
2102
|
+
handleNotFoundError = null,
|
|
2103
|
+
handlePermissionDeniedError = null,
|
|
2104
|
+
} = {},
|
|
2105
|
+
) => {
|
|
2106
|
+
const nodeMethod = followLink ? stat : lstat;
|
|
2107
|
+
|
|
2108
|
+
return new Promise((resolve, reject) => {
|
|
2109
|
+
nodeMethod(sourcePath, (error, statsObject) => {
|
|
2110
|
+
if (error) {
|
|
2111
|
+
if (handleNotFoundError && error.code === "ENOENT") {
|
|
2112
|
+
resolve(handleNotFoundError(error));
|
|
2113
|
+
} else if (
|
|
2114
|
+
handlePermissionDeniedError &&
|
|
2115
|
+
(error.code === "EPERM" || error.code === "EACCES")
|
|
2116
|
+
) {
|
|
2117
|
+
resolve(handlePermissionDeniedError(error));
|
|
2118
|
+
} else {
|
|
2119
|
+
reject(error);
|
|
2120
|
+
}
|
|
2121
|
+
} else {
|
|
2122
|
+
resolve(statsObject);
|
|
2123
|
+
}
|
|
2124
|
+
});
|
|
2125
|
+
});
|
|
2126
|
+
};
|
|
2127
|
+
|
|
2128
|
+
const statsToType = (stats) => {
|
|
2129
|
+
if (stats.isFile()) return "file";
|
|
2130
|
+
if (stats.isDirectory()) return "directory";
|
|
2131
|
+
if (stats.isSymbolicLink()) return "symbolic-link";
|
|
2132
|
+
if (stats.isFIFO()) return "fifo";
|
|
2133
|
+
if (stats.isSocket()) return "socket";
|
|
2134
|
+
if (stats.isCharacterDevice()) return "character-device";
|
|
2135
|
+
if (stats.isBlockDevice()) return "block-device";
|
|
2136
|
+
return undefined;
|
|
2137
|
+
};
|
|
2138
|
+
|
|
2139
|
+
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_fspromises_mkdir_path_options
|
|
2140
|
+
const { mkdir } = promises;
|
|
2141
|
+
|
|
2142
|
+
const writeDirectory = async (
|
|
2143
|
+
destination,
|
|
2144
|
+
{ recursive = true, allowUseless = false } = {},
|
|
2145
|
+
) => {
|
|
2146
|
+
const destinationUrl = assertAndNormalizeDirectoryUrl(destination);
|
|
2147
|
+
const destinationPath = urlToFileSystemPath(destinationUrl);
|
|
2148
|
+
|
|
2149
|
+
const destinationStats = await readEntryStat(destinationUrl, {
|
|
2150
|
+
nullIfNotFound: true,
|
|
2151
|
+
followLink: false,
|
|
2152
|
+
});
|
|
2153
|
+
|
|
2154
|
+
if (destinationStats) {
|
|
2155
|
+
if (destinationStats.isDirectory()) {
|
|
2156
|
+
if (allowUseless) {
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
throw new Error(`directory already exists at ${destinationPath}`);
|
|
2160
|
+
}
|
|
2119
2161
|
|
|
2120
2162
|
const destinationType = statsToType(destinationStats);
|
|
2121
2163
|
throw new Error(
|
|
@@ -2133,6 +2175,23 @@ const writeDirectory = async (
|
|
|
2133
2175
|
}
|
|
2134
2176
|
};
|
|
2135
2177
|
|
|
2178
|
+
const writeFileSync = (destination, content = "") => {
|
|
2179
|
+
const destinationUrl = assertAndNormalizeFileUrl(destination);
|
|
2180
|
+
const destinationUrlObject = new URL(destinationUrl);
|
|
2181
|
+
try {
|
|
2182
|
+
writeFileSync$1(destinationUrlObject, content);
|
|
2183
|
+
} catch (error) {
|
|
2184
|
+
if (error.code === "ENOENT") {
|
|
2185
|
+
mkdirSync(new URL("./", destinationUrlObject), {
|
|
2186
|
+
recursive: true,
|
|
2187
|
+
});
|
|
2188
|
+
writeFileSync$1(destinationUrlObject, content);
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
throw error;
|
|
2192
|
+
}
|
|
2193
|
+
};
|
|
2194
|
+
|
|
2136
2195
|
const removeEntry = async (
|
|
2137
2196
|
source,
|
|
2138
2197
|
{
|
|
@@ -2378,100 +2437,231 @@ const removeDirectoryNaive = (
|
|
|
2378
2437
|
});
|
|
2379
2438
|
};
|
|
2380
2439
|
|
|
2381
|
-
|
|
2382
|
-
const stats = await readEntryStat(source, {
|
|
2383
|
-
nullIfNotFound: true,
|
|
2384
|
-
followLink: false,
|
|
2385
|
-
});
|
|
2386
|
-
if (stats === null) {
|
|
2387
|
-
// if there is nothing, create a directory
|
|
2388
|
-
return writeDirectory(source, { allowUseless: true });
|
|
2389
|
-
}
|
|
2390
|
-
if (stats.isDirectory()) {
|
|
2391
|
-
// if there is a directory remove its content and done
|
|
2392
|
-
return removeEntry(source, {
|
|
2393
|
-
allowUseless: true,
|
|
2394
|
-
recursive: true,
|
|
2395
|
-
onlyContent: true,
|
|
2396
|
-
});
|
|
2397
|
-
}
|
|
2440
|
+
process.platform === "win32";
|
|
2398
2441
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
);
|
|
2404
|
-
};
|
|
2442
|
+
/*
|
|
2443
|
+
* - stats object documentation on Node.js
|
|
2444
|
+
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
|
|
2445
|
+
*/
|
|
2405
2446
|
|
|
2406
|
-
const isWindows$2 = process.platform === "win32";
|
|
2407
|
-
const baseUrlFallback = fileSystemPathToUrl$1(process.cwd());
|
|
2408
2447
|
|
|
2409
|
-
|
|
2410
|
-
* Some url might be resolved or remapped to url without the windows drive letter.
|
|
2411
|
-
* For instance
|
|
2412
|
-
* new URL('/foo.js', 'file:///C:/dir/file.js')
|
|
2413
|
-
* resolves to
|
|
2414
|
-
* 'file:///foo.js'
|
|
2415
|
-
*
|
|
2416
|
-
* But on windows it becomes a problem because we need the drive letter otherwise
|
|
2417
|
-
* url cannot be converted to a filesystem path.
|
|
2418
|
-
*
|
|
2419
|
-
* ensureWindowsDriveLetter ensure a resolved url still contains the drive letter.
|
|
2420
|
-
*/
|
|
2448
|
+
process.platform === "win32";
|
|
2421
2449
|
|
|
2422
|
-
const
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2450
|
+
const mediaTypeInfos = {
|
|
2451
|
+
"application/json": {
|
|
2452
|
+
extensions: ["json"],
|
|
2453
|
+
isTextual: true,
|
|
2454
|
+
},
|
|
2455
|
+
"application/importmap+json": {
|
|
2456
|
+
extensions: ["importmap"],
|
|
2457
|
+
isTextual: true,
|
|
2458
|
+
},
|
|
2459
|
+
"application/manifest+json": {
|
|
2460
|
+
extensions: ["webmanifest"],
|
|
2461
|
+
isTextual: true,
|
|
2462
|
+
},
|
|
2463
|
+
"application/octet-stream": {},
|
|
2464
|
+
"application/pdf": {
|
|
2465
|
+
extensions: ["pdf"],
|
|
2466
|
+
},
|
|
2467
|
+
"application/xml": {
|
|
2468
|
+
extensions: ["xml"],
|
|
2469
|
+
},
|
|
2470
|
+
"application/x-gzip": {
|
|
2471
|
+
extensions: ["gz"],
|
|
2472
|
+
},
|
|
2473
|
+
"application/wasm": {
|
|
2474
|
+
extensions: ["wasm"],
|
|
2475
|
+
},
|
|
2476
|
+
"application/zip": {
|
|
2477
|
+
extensions: ["zip"],
|
|
2478
|
+
},
|
|
2479
|
+
"audio/basic": {
|
|
2480
|
+
extensions: ["au", "snd"],
|
|
2481
|
+
},
|
|
2482
|
+
"audio/mpeg": {
|
|
2483
|
+
extensions: ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"],
|
|
2484
|
+
},
|
|
2485
|
+
"audio/midi": {
|
|
2486
|
+
extensions: ["midi", "mid", "kar", "rmi"],
|
|
2487
|
+
},
|
|
2488
|
+
"audio/mp4": {
|
|
2489
|
+
extensions: ["m4a", "mp4a"],
|
|
2490
|
+
},
|
|
2491
|
+
"audio/ogg": {
|
|
2492
|
+
extensions: ["oga", "ogg", "spx"],
|
|
2493
|
+
},
|
|
2494
|
+
"audio/webm": {
|
|
2495
|
+
extensions: ["weba"],
|
|
2496
|
+
},
|
|
2497
|
+
"audio/x-wav": {
|
|
2498
|
+
extensions: ["wav"],
|
|
2499
|
+
},
|
|
2500
|
+
"font/ttf": {
|
|
2501
|
+
extensions: ["ttf"],
|
|
2502
|
+
},
|
|
2503
|
+
"font/woff": {
|
|
2504
|
+
extensions: ["woff"],
|
|
2505
|
+
},
|
|
2506
|
+
"font/woff2": {
|
|
2507
|
+
extensions: ["woff2"],
|
|
2508
|
+
},
|
|
2509
|
+
"image/png": {
|
|
2510
|
+
extensions: ["png"],
|
|
2511
|
+
},
|
|
2512
|
+
"image/gif": {
|
|
2513
|
+
extensions: ["gif"],
|
|
2514
|
+
},
|
|
2515
|
+
"image/jpeg": {
|
|
2516
|
+
extensions: ["jpg"],
|
|
2517
|
+
},
|
|
2518
|
+
"image/svg+xml": {
|
|
2519
|
+
extensions: ["svg", "svgz"],
|
|
2520
|
+
isTextual: true,
|
|
2521
|
+
},
|
|
2522
|
+
"text/plain": {
|
|
2523
|
+
extensions: ["txt"],
|
|
2524
|
+
},
|
|
2525
|
+
"text/html": {
|
|
2526
|
+
extensions: ["html"],
|
|
2527
|
+
},
|
|
2528
|
+
"text/css": {
|
|
2529
|
+
extensions: ["css"],
|
|
2530
|
+
},
|
|
2531
|
+
"text/javascript": {
|
|
2532
|
+
extensions: ["js", "cjs", "mjs", "ts", "jsx", "tsx"],
|
|
2533
|
+
},
|
|
2534
|
+
"text/x-sass": {
|
|
2535
|
+
extensions: ["sass"],
|
|
2536
|
+
},
|
|
2537
|
+
"text/x-scss": {
|
|
2538
|
+
extensions: ["scss"],
|
|
2539
|
+
},
|
|
2540
|
+
"text/cache-manifest": {
|
|
2541
|
+
extensions: ["appcache"],
|
|
2542
|
+
},
|
|
2543
|
+
"video/mp4": {
|
|
2544
|
+
extensions: ["mp4", "mp4v", "mpg4"],
|
|
2545
|
+
},
|
|
2546
|
+
"video/mpeg": {
|
|
2547
|
+
extensions: ["mpeg", "mpg", "mpe", "m1v", "m2v"],
|
|
2548
|
+
},
|
|
2549
|
+
"video/ogg": {
|
|
2550
|
+
extensions: ["ogv"],
|
|
2551
|
+
},
|
|
2552
|
+
"video/webm": {
|
|
2553
|
+
extensions: ["webm"],
|
|
2554
|
+
},
|
|
2555
|
+
};
|
|
2428
2556
|
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2557
|
+
const CONTENT_TYPE = {
|
|
2558
|
+
parse: (string) => {
|
|
2559
|
+
const [mediaType, charset] = string.split(";");
|
|
2560
|
+
return { mediaType: normalizeMediaType(mediaType), charset };
|
|
2561
|
+
},
|
|
2432
2562
|
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
}
|
|
2563
|
+
stringify: ({ mediaType, charset }) => {
|
|
2564
|
+
if (charset) {
|
|
2565
|
+
return `${mediaType};${charset}`;
|
|
2566
|
+
}
|
|
2567
|
+
return mediaType;
|
|
2568
|
+
},
|
|
2440
2569
|
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2570
|
+
asMediaType: (value) => {
|
|
2571
|
+
if (typeof value === "string") {
|
|
2572
|
+
return CONTENT_TYPE.parse(value).mediaType;
|
|
2573
|
+
}
|
|
2574
|
+
if (typeof value === "object") {
|
|
2575
|
+
return value.mediaType;
|
|
2576
|
+
}
|
|
2577
|
+
return null;
|
|
2578
|
+
},
|
|
2449
2579
|
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
baseUrlOrFallback.slice("file://".length),
|
|
2456
|
-
);
|
|
2457
|
-
if (!driveLetter) {
|
|
2458
|
-
throw new Error(
|
|
2459
|
-
`drive letter expected on baseUrl but got ${baseUrl} to ensure windows drive letter on ${url}`,
|
|
2580
|
+
isJson: (value) => {
|
|
2581
|
+
const mediaType = CONTENT_TYPE.asMediaType(value);
|
|
2582
|
+
return (
|
|
2583
|
+
mediaType === "application/json" ||
|
|
2584
|
+
/^application\/\w+\+json$/.test(mediaType)
|
|
2460
2585
|
);
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2586
|
+
},
|
|
2587
|
+
|
|
2588
|
+
isTextual: (value) => {
|
|
2589
|
+
const mediaType = CONTENT_TYPE.asMediaType(value);
|
|
2590
|
+
if (mediaType.startsWith("text/")) {
|
|
2591
|
+
return true;
|
|
2592
|
+
}
|
|
2593
|
+
const mediaTypeInfo = mediaTypeInfos[mediaType];
|
|
2594
|
+
if (mediaTypeInfo && mediaTypeInfo.isTextual) {
|
|
2595
|
+
return true;
|
|
2596
|
+
}
|
|
2597
|
+
// catch things like application/manifest+json, application/importmap+json
|
|
2598
|
+
if (/^application\/\w+\+json$/.test(mediaType)) {
|
|
2599
|
+
return true;
|
|
2600
|
+
}
|
|
2601
|
+
return false;
|
|
2602
|
+
},
|
|
2603
|
+
|
|
2604
|
+
isBinary: (value) => !CONTENT_TYPE.isTextual(value),
|
|
2605
|
+
|
|
2606
|
+
asFileExtension: (value) => {
|
|
2607
|
+
const mediaType = CONTENT_TYPE.asMediaType(value);
|
|
2608
|
+
const mediaTypeInfo = mediaTypeInfos[mediaType];
|
|
2609
|
+
return mediaTypeInfo ? `.${mediaTypeInfo.extensions[0]}` : "";
|
|
2610
|
+
},
|
|
2611
|
+
|
|
2612
|
+
fromUrlExtension: (url) => {
|
|
2613
|
+
const { pathname } = new URL(url);
|
|
2614
|
+
const extensionWithDot = extname(pathname);
|
|
2615
|
+
if (!extensionWithDot || extensionWithDot === ".") {
|
|
2616
|
+
return "application/octet-stream";
|
|
2617
|
+
}
|
|
2618
|
+
const extension = extensionWithDot.slice(1);
|
|
2619
|
+
const mediaTypeFound = Object.keys(mediaTypeInfos).find((mediaType) => {
|
|
2620
|
+
const mediaTypeInfo = mediaTypeInfos[mediaType];
|
|
2621
|
+
return (
|
|
2622
|
+
mediaTypeInfo.extensions && mediaTypeInfo.extensions.includes(extension)
|
|
2623
|
+
);
|
|
2624
|
+
});
|
|
2625
|
+
return mediaTypeFound || "application/octet-stream";
|
|
2626
|
+
},
|
|
2463
2627
|
};
|
|
2464
2628
|
|
|
2465
|
-
const
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
return resource[1];
|
|
2629
|
+
const normalizeMediaType = (value) => {
|
|
2630
|
+
if (value === "application/javascript") {
|
|
2631
|
+
return "text/javascript";
|
|
2469
2632
|
}
|
|
2470
|
-
return
|
|
2633
|
+
return value;
|
|
2471
2634
|
};
|
|
2472
2635
|
|
|
2473
2636
|
process.platform === "win32";
|
|
2474
2637
|
|
|
2638
|
+
const ensureEmptyDirectory = async (source) => {
|
|
2639
|
+
const stats = await readEntryStat(source, {
|
|
2640
|
+
nullIfNotFound: true,
|
|
2641
|
+
followLink: false,
|
|
2642
|
+
});
|
|
2643
|
+
if (stats === null) {
|
|
2644
|
+
// if there is nothing, create a directory
|
|
2645
|
+
await writeDirectory(source, { allowUseless: true });
|
|
2646
|
+
return;
|
|
2647
|
+
}
|
|
2648
|
+
if (stats.isDirectory()) {
|
|
2649
|
+
// if there is a directory remove its content and done
|
|
2650
|
+
await removeEntry(source, {
|
|
2651
|
+
allowUseless: true,
|
|
2652
|
+
recursive: true,
|
|
2653
|
+
onlyContent: true,
|
|
2654
|
+
});
|
|
2655
|
+
return;
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
const sourceType = statsToType(stats);
|
|
2659
|
+
const sourcePath = urlToFileSystemPath(assertAndNormalizeFileUrl(source));
|
|
2660
|
+
throw new Error(
|
|
2661
|
+
`ensureEmptyDirectory expect directory at ${sourcePath}, found ${sourceType} instead`,
|
|
2662
|
+
);
|
|
2663
|
+
};
|
|
2664
|
+
|
|
2475
2665
|
const guardTooFastSecondCallPerFile = (
|
|
2476
2666
|
callback,
|
|
2477
2667
|
cooldownBetweenFileEvents = 40,
|
|
@@ -2917,21 +3107,33 @@ const fileSystemPathToDirectoryRelativeUrlAndFilename = (path) => {
|
|
|
2917
3107
|
};
|
|
2918
3108
|
};
|
|
2919
3109
|
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
}
|
|
2933
|
-
|
|
3110
|
+
/*
|
|
3111
|
+
* - Buffer documentation on Node.js
|
|
3112
|
+
* https://nodejs.org/docs/latest-v13.x/api/buffer.html
|
|
3113
|
+
* - eTag documentation on MDN
|
|
3114
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
|
|
3115
|
+
*/
|
|
3116
|
+
|
|
3117
|
+
|
|
3118
|
+
const ETAG_FOR_EMPTY_CONTENT$1 = '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
|
|
3119
|
+
|
|
3120
|
+
const bufferToEtag$1 = (buffer) => {
|
|
3121
|
+
if (!Buffer.isBuffer(buffer)) {
|
|
3122
|
+
throw new TypeError(`buffer expected, got ${buffer}`);
|
|
3123
|
+
}
|
|
3124
|
+
|
|
3125
|
+
if (buffer.length === 0) {
|
|
3126
|
+
return ETAG_FOR_EMPTY_CONTENT$1;
|
|
2934
3127
|
}
|
|
3128
|
+
|
|
3129
|
+
const hash = createHash("sha1");
|
|
3130
|
+
hash.update(buffer, "utf8");
|
|
3131
|
+
|
|
3132
|
+
const hashBase64String = hash.digest("base64");
|
|
3133
|
+
const hashBase64StringSubset = hashBase64String.slice(0, 27);
|
|
3134
|
+
const length = buffer.length;
|
|
3135
|
+
|
|
3136
|
+
return `"${length.toString(16)}-${hashBase64StringSubset}"`;
|
|
2935
3137
|
};
|
|
2936
3138
|
|
|
2937
3139
|
const LOG_LEVEL_OFF = "off";
|
|
@@ -6914,192 +7116,6 @@ const PROCESS_TEARDOWN_EVENTS_MAP = {
|
|
|
6914
7116
|
exit: STOP_REASON_PROCESS_EXIT,
|
|
6915
7117
|
};
|
|
6916
7118
|
|
|
6917
|
-
const mediaTypeInfos = {
|
|
6918
|
-
"application/json": {
|
|
6919
|
-
extensions: ["json"],
|
|
6920
|
-
isTextual: true,
|
|
6921
|
-
},
|
|
6922
|
-
"application/importmap+json": {
|
|
6923
|
-
extensions: ["importmap"],
|
|
6924
|
-
isTextual: true,
|
|
6925
|
-
},
|
|
6926
|
-
"application/manifest+json": {
|
|
6927
|
-
extensions: ["webmanifest"],
|
|
6928
|
-
isTextual: true,
|
|
6929
|
-
},
|
|
6930
|
-
"application/octet-stream": {},
|
|
6931
|
-
"application/pdf": {
|
|
6932
|
-
extensions: ["pdf"],
|
|
6933
|
-
},
|
|
6934
|
-
"application/xml": {
|
|
6935
|
-
extensions: ["xml"],
|
|
6936
|
-
},
|
|
6937
|
-
"application/x-gzip": {
|
|
6938
|
-
extensions: ["gz"],
|
|
6939
|
-
},
|
|
6940
|
-
"application/wasm": {
|
|
6941
|
-
extensions: ["wasm"],
|
|
6942
|
-
},
|
|
6943
|
-
"application/zip": {
|
|
6944
|
-
extensions: ["zip"],
|
|
6945
|
-
},
|
|
6946
|
-
"audio/basic": {
|
|
6947
|
-
extensions: ["au", "snd"],
|
|
6948
|
-
},
|
|
6949
|
-
"audio/mpeg": {
|
|
6950
|
-
extensions: ["mpga", "mp2", "mp2a", "mp3", "m2a", "m3a"],
|
|
6951
|
-
},
|
|
6952
|
-
"audio/midi": {
|
|
6953
|
-
extensions: ["midi", "mid", "kar", "rmi"],
|
|
6954
|
-
},
|
|
6955
|
-
"audio/mp4": {
|
|
6956
|
-
extensions: ["m4a", "mp4a"],
|
|
6957
|
-
},
|
|
6958
|
-
"audio/ogg": {
|
|
6959
|
-
extensions: ["oga", "ogg", "spx"],
|
|
6960
|
-
},
|
|
6961
|
-
"audio/webm": {
|
|
6962
|
-
extensions: ["weba"],
|
|
6963
|
-
},
|
|
6964
|
-
"audio/x-wav": {
|
|
6965
|
-
extensions: ["wav"],
|
|
6966
|
-
},
|
|
6967
|
-
"font/ttf": {
|
|
6968
|
-
extensions: ["ttf"],
|
|
6969
|
-
},
|
|
6970
|
-
"font/woff": {
|
|
6971
|
-
extensions: ["woff"],
|
|
6972
|
-
},
|
|
6973
|
-
"font/woff2": {
|
|
6974
|
-
extensions: ["woff2"],
|
|
6975
|
-
},
|
|
6976
|
-
"image/png": {
|
|
6977
|
-
extensions: ["png"],
|
|
6978
|
-
},
|
|
6979
|
-
"image/gif": {
|
|
6980
|
-
extensions: ["gif"],
|
|
6981
|
-
},
|
|
6982
|
-
"image/jpeg": {
|
|
6983
|
-
extensions: ["jpg"],
|
|
6984
|
-
},
|
|
6985
|
-
"image/svg+xml": {
|
|
6986
|
-
extensions: ["svg", "svgz"],
|
|
6987
|
-
isTextual: true,
|
|
6988
|
-
},
|
|
6989
|
-
"text/plain": {
|
|
6990
|
-
extensions: ["txt"],
|
|
6991
|
-
},
|
|
6992
|
-
"text/html": {
|
|
6993
|
-
extensions: ["html"],
|
|
6994
|
-
},
|
|
6995
|
-
"text/css": {
|
|
6996
|
-
extensions: ["css"],
|
|
6997
|
-
},
|
|
6998
|
-
"text/javascript": {
|
|
6999
|
-
extensions: ["js", "cjs", "mjs", "ts", "jsx", "tsx"],
|
|
7000
|
-
},
|
|
7001
|
-
"text/x-sass": {
|
|
7002
|
-
extensions: ["sass"],
|
|
7003
|
-
},
|
|
7004
|
-
"text/x-scss": {
|
|
7005
|
-
extensions: ["scss"],
|
|
7006
|
-
},
|
|
7007
|
-
"text/cache-manifest": {
|
|
7008
|
-
extensions: ["appcache"],
|
|
7009
|
-
},
|
|
7010
|
-
"video/mp4": {
|
|
7011
|
-
extensions: ["mp4", "mp4v", "mpg4"],
|
|
7012
|
-
},
|
|
7013
|
-
"video/mpeg": {
|
|
7014
|
-
extensions: ["mpeg", "mpg", "mpe", "m1v", "m2v"],
|
|
7015
|
-
},
|
|
7016
|
-
"video/ogg": {
|
|
7017
|
-
extensions: ["ogv"],
|
|
7018
|
-
},
|
|
7019
|
-
"video/webm": {
|
|
7020
|
-
extensions: ["webm"],
|
|
7021
|
-
},
|
|
7022
|
-
};
|
|
7023
|
-
|
|
7024
|
-
const CONTENT_TYPE = {
|
|
7025
|
-
parse: (string) => {
|
|
7026
|
-
const [mediaType, charset] = string.split(";");
|
|
7027
|
-
return { mediaType: normalizeMediaType(mediaType), charset };
|
|
7028
|
-
},
|
|
7029
|
-
|
|
7030
|
-
stringify: ({ mediaType, charset }) => {
|
|
7031
|
-
if (charset) {
|
|
7032
|
-
return `${mediaType};${charset}`;
|
|
7033
|
-
}
|
|
7034
|
-
return mediaType;
|
|
7035
|
-
},
|
|
7036
|
-
|
|
7037
|
-
asMediaType: (value) => {
|
|
7038
|
-
if (typeof value === "string") {
|
|
7039
|
-
return CONTENT_TYPE.parse(value).mediaType;
|
|
7040
|
-
}
|
|
7041
|
-
if (typeof value === "object") {
|
|
7042
|
-
return value.mediaType;
|
|
7043
|
-
}
|
|
7044
|
-
return null;
|
|
7045
|
-
},
|
|
7046
|
-
|
|
7047
|
-
isJson: (value) => {
|
|
7048
|
-
const mediaType = CONTENT_TYPE.asMediaType(value);
|
|
7049
|
-
return (
|
|
7050
|
-
mediaType === "application/json" ||
|
|
7051
|
-
/^application\/\w+\+json$/.test(mediaType)
|
|
7052
|
-
);
|
|
7053
|
-
},
|
|
7054
|
-
|
|
7055
|
-
isTextual: (value) => {
|
|
7056
|
-
const mediaType = CONTENT_TYPE.asMediaType(value);
|
|
7057
|
-
if (mediaType.startsWith("text/")) {
|
|
7058
|
-
return true;
|
|
7059
|
-
}
|
|
7060
|
-
const mediaTypeInfo = mediaTypeInfos[mediaType];
|
|
7061
|
-
if (mediaTypeInfo && mediaTypeInfo.isTextual) {
|
|
7062
|
-
return true;
|
|
7063
|
-
}
|
|
7064
|
-
// catch things like application/manifest+json, application/importmap+json
|
|
7065
|
-
if (/^application\/\w+\+json$/.test(mediaType)) {
|
|
7066
|
-
return true;
|
|
7067
|
-
}
|
|
7068
|
-
return false;
|
|
7069
|
-
},
|
|
7070
|
-
|
|
7071
|
-
isBinary: (value) => !CONTENT_TYPE.isTextual(value),
|
|
7072
|
-
|
|
7073
|
-
asFileExtension: (value) => {
|
|
7074
|
-
const mediaType = CONTENT_TYPE.asMediaType(value);
|
|
7075
|
-
const mediaTypeInfo = mediaTypeInfos[mediaType];
|
|
7076
|
-
return mediaTypeInfo ? `.${mediaTypeInfo.extensions[0]}` : "";
|
|
7077
|
-
},
|
|
7078
|
-
|
|
7079
|
-
fromUrlExtension: (url) => {
|
|
7080
|
-
const { pathname } = new URL(url);
|
|
7081
|
-
const extensionWithDot = extname(pathname);
|
|
7082
|
-
if (!extensionWithDot || extensionWithDot === ".") {
|
|
7083
|
-
return "application/octet-stream";
|
|
7084
|
-
}
|
|
7085
|
-
const extension = extensionWithDot.slice(1);
|
|
7086
|
-
const mediaTypeFound = Object.keys(mediaTypeInfos).find((mediaType) => {
|
|
7087
|
-
const mediaTypeInfo = mediaTypeInfos[mediaType];
|
|
7088
|
-
return (
|
|
7089
|
-
mediaTypeInfo.extensions && mediaTypeInfo.extensions.includes(extension)
|
|
7090
|
-
);
|
|
7091
|
-
});
|
|
7092
|
-
return mediaTypeFound || "application/octet-stream";
|
|
7093
|
-
},
|
|
7094
|
-
};
|
|
7095
|
-
|
|
7096
|
-
const normalizeMediaType = (value) => {
|
|
7097
|
-
if (value === "application/javascript") {
|
|
7098
|
-
return "text/javascript";
|
|
7099
|
-
}
|
|
7100
|
-
return value;
|
|
7101
|
-
};
|
|
7102
|
-
|
|
7103
7119
|
const isFileSystemPath = (value) => {
|
|
7104
7120
|
if (typeof value !== "string") {
|
|
7105
7121
|
throw new TypeError(
|