Files
2020-03-02 14:53:23 +07:00

1578 lines
48 KiB
JavaScript

/* eslint-disable curly */
/* eslint-disable new-cap */
/* eslint-disable no-buffer-constructor */
/* eslint-disable no-multi-spaces */
/* eslint-disable no-underscore-dangle */
/* eslint-disable prefer-rest-params */
/* eslint-disable prefer-spread */
/* global EXECPATH_FD */
/* global PAYLOAD_POSITION */
/* global PAYLOAD_SIZE */
/* global REQUIRE_COMMON */
/* global VIRTUAL_FILESYSTEM */
/* global DEFAULT_ENTRYPOINT */
'use strict';
var common = {};
REQUIRE_COMMON(common);
var STORE_BLOB = common.STORE_BLOB;
var STORE_CONTENT = common.STORE_CONTENT;
var STORE_LINKS = common.STORE_LINKS;
var STORE_STAT = common.STORE_STAT;
var normalizePath = common.normalizePath;
var insideSnapshot = common.insideSnapshot;
var stripSnapshot = common.stripSnapshot;
var removeUplevels = common.removeUplevels;
var FLAG_ENABLE_PROJECT = false;
var NODE_VERSION_MAJOR = process.version.match(/^v(\d+)/)[1] | 0;
// /////////////////////////////////////////////////////////////////
// ENTRYPOINT //////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
// set ENTRYPOINT and ARGV0 here because
// they can be altered during process run
var ARGV0 = process.argv[0];
var EXECPATH = process.execPath;
var ENTRYPOINT = process.argv[1];
if (process.env.PKG_EXECPATH === 'PKG_INVOKE_NODEJS') {
return { undoPatch: true };
}
if (NODE_VERSION_MAJOR < 12 || require('worker_threads').isMainThread) {
if (process.argv[1] !== 'PKG_DUMMY_ENTRYPOINT') {
// expand once patchless is introduced, that
// will obviously lack any work in node_main.cc
throw new Error('PKG_DUMMY_ENTRYPOINT EXPECTED');
}
}
if (process.env.PKG_EXECPATH === EXECPATH) {
process.argv.splice(1, 1);
if (process.argv[1] && process.argv[1] !== '-') {
// https://github.com/nodejs/node/blob/1a96d83a223ff9f05f7d942fb84440d323f7b596/lib/internal/bootstrap/node.js#L269
process.argv[1] = require('path').resolve(process.argv[1]);
}
} else {
process.argv[1] = DEFAULT_ENTRYPOINT;
}
ENTRYPOINT = process.argv[1];
delete process.env.PKG_EXECPATH;
// /////////////////////////////////////////////////////////////////
// EXECSTAT ////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
var EXECSTAT = require('fs').statSync(EXECPATH);
EXECSTAT.atimeMs = EXECSTAT.atime.getTime();
EXECSTAT.mtimeMs = EXECSTAT.mtime.getTime();
EXECSTAT.ctimeMs = EXECSTAT.ctime.getTime();
EXECSTAT.birthtimeMs = EXECSTAT.birthtime.getTime();
// /////////////////////////////////////////////////////////////////
// MOUNTPOINTS /////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
var mountpoints = [];
function insideMountpoint (f) {
if (!insideSnapshot(f)) return null;
var file = normalizePath(f);
var found = mountpoints.map(function (mountpoint) {
var interior = mountpoint.interior;
var exterior = mountpoint.exterior;
if (interior === file) return exterior;
var left = interior + require('path').sep;
if (file.slice(0, left.length) !== left) return null;
return exterior + file.slice(left.length - 1);
}).filter(function (result) {
return result;
});
if (found.length >= 2) throw new Error('UNEXPECTED-00');
if (found.length === 0) return null;
return found[0];
}
function readdirMountpoints (path) {
return mountpoints.map(function (mountpoint) {
return mountpoint.interior;
}).filter(function (interior) {
return require('path').dirname(interior) === path;
}).map(function (interior) {
return require('path').basename(interior);
});
}
function translate (f) {
var result = insideMountpoint(f);
if (!result) throw new Error('UNEXPECTED-05');
return result;
}
function cloneArgs (args_) {
return Array.prototype.slice.call(args_);
}
function translateNth (args_, index, f) {
var args = cloneArgs(args_);
args[index] = translate(f);
return args;
}
function createMountpoint (interior, exterior) {
// TODO validate
mountpoints.push({ interior: interior, exterior: exterior });
}
/*
// TODO move to some test
createMountpoint("d:\\snapshot\\countly\\plugins-ext", "d:\\deploy\\countly\\v16.02\\plugins-ext");
console.log(insideMountpoint("d:\\snapshot"));
console.log(insideMountpoint("d:\\snapshot\\"));
console.log(insideMountpoint("d:\\snapshot\\countly"));
console.log(insideMountpoint("d:\\snapshot\\countly\\"));
console.log(insideMountpoint("d:\\snapshot\\countly\\plugins-ext"));
console.log(insideMountpoint("d:\\snapshot\\countly\\plugins-ext\\"));
console.log(insideMountpoint("d:\\snapshot\\countly\\plugins-ext\\1234"));
console.log(translate("d:\\snapshot\\countly\\plugins-ext"));
console.log(translate("d:\\snapshot\\countly\\plugins-ext\\"));
console.log(translate("d:\\snapshot\\countly\\plugins-ext\\1234"));
console.log(translateNth([], 0, "d:\\snapshot\\countly\\plugins-ext"));
console.log(translateNth([], 0, "d:\\snapshot\\countly\\plugins-ext\\"));
console.log(translateNth([], 0, "d:\\snapshot\\countly\\plugins-ext\\1234"));
console.log(translateNth(["", "r+"], 0, "d:\\snapshot\\countly\\plugins-ext"));
console.log(translateNth(["", "rw"], 0, "d:\\snapshot\\countly\\plugins-ext\\"));
console.log(translateNth(["", "a+"], 0, "d:\\snapshot\\countly\\plugins-ext\\1234"));
*/
// /////////////////////////////////////////////////////////////////
// PROJECT /////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
function isRootPath (p) {
if (p === '.') p = require('path').resolve(p);
return require('path').dirname(p) === p;
}
function projectToFilesystem (f) {
var xpdn = require('path').dirname(
EXECPATH
);
var relatives = [];
relatives.push(
removeUplevels(
require('path').relative(
require('path').dirname(
DEFAULT_ENTRYPOINT
), f
)
)
);
if (relatives[0].slice(0, 'node_modules'.length) === 'node_modules') {
// one more relative without starting 'node_modules'
relatives.push(relatives[0].slice('node_modules'.length + 1));
}
var uplevels = [];
var maxUplevels = xpdn.split(require('path').sep).length;
for (var i = 0, u = ''; i < maxUplevels; i += 1) {
uplevels.push(u);
u += '/..';
}
var results = [];
uplevels.forEach(function (uplevel) {
relatives.forEach(function (relative) {
results.push(require('path').join(
xpdn,
uplevel,
relative
));
});
});
return results;
}
function projectToNearby (f) {
return require('path').join(
require('path').dirname(
EXECPATH
),
require('path').basename(
f
)
);
}
function findNativeAddonSyncFreeFromRequire (path) {
if (!insideSnapshot(path)) throw new Error('UNEXPECTED-10');
if (path.slice(-5) !== '.node') return null; // leveldown.node.js
// check mearby first to prevent .node tampering
var projector = projectToNearby(path);
if (require('fs').existsSync(projector)) return projector;
var projectors = projectToFilesystem(path);
for (var i = 0; i < projectors.length; i += 1) {
if (require('fs').existsSync(projectors[i])) return projectors[i];
}
return null;
}
function findNativeAddonSyncUnderRequire (path) {
if (!FLAG_ENABLE_PROJECT) return null;
return findNativeAddonSyncFreeFromRequire(path);
}
// /////////////////////////////////////////////////////////////////
// FLOW UTILS //////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
function asap (cb) {
process.nextTick(cb);
}
function dezalgo (cb) {
if (!cb) return cb;
var sync = true;
asap(function () {
sync = false;
});
return function zalgoSafe () {
var args = arguments;
if (sync) {
asap(function () {
cb.apply(undefined, args);
});
} else {
cb.apply(undefined, args);
}
};
}
function rethrow (error, arg) {
if (error) throw error;
return arg;
}
// /////////////////////////////////////////////////////////////////
// PAYLOAD /////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
if (typeof PAYLOAD_POSITION !== 'number' ||
typeof PAYLOAD_SIZE !== 'number') {
throw new Error('MUST HAVE PAYLOAD');
}
var readPayload = function (buffer, offset, length, position, callback) {
require('fs').read(EXECPATH_FD,
buffer, offset, length, PAYLOAD_POSITION + position, callback);
};
var readPayloadSync = function (buffer, offset, length, position) {
return require('fs').readSync(EXECPATH_FD,
buffer, offset, length, PAYLOAD_POSITION + position);
};
function payloadCopyUni (source, target, targetStart, sourceStart, sourceEnd, cb) {
var cb2 = cb || rethrow;
if (sourceStart >= source[1]) return cb2(null, 0);
if (sourceEnd >= source[1]) sourceEnd = source[1];
var payloadPos = source[0] + sourceStart;
var targetPos = targetStart;
var targetEnd = targetStart + sourceEnd - sourceStart;
if (cb) {
readPayload(target, targetPos, targetEnd - targetPos, payloadPos, cb);
} else {
return readPayloadSync(target, targetPos, targetEnd - targetPos, payloadPos);
}
}
function payloadCopyMany (source, target, targetStart, sourceStart, cb) {
var payloadPos = source[0] + sourceStart;
var targetPos = targetStart;
var targetEnd = targetStart + source[1] - sourceStart;
readPayload(target, targetPos, targetEnd - targetPos, payloadPos, function (error, chunkSize) {
if (error) return cb(error);
sourceStart += chunkSize;
targetPos += chunkSize;
if (chunkSize !== 0 && targetPos < targetEnd) {
payloadCopyMany(source, target, targetPos, sourceStart, cb);
} else {
return cb();
}
});
}
function payloadCopyManySync (source, target, targetStart, sourceStart) {
var payloadPos = source[0] + sourceStart;
var targetPos = targetStart;
var targetEnd = targetStart + source[1] - sourceStart;
var chunkSize;
while (true) {
chunkSize = readPayloadSync(target, targetPos, targetEnd - targetPos, payloadPos);
payloadPos += chunkSize;
targetPos += chunkSize;
if (!(chunkSize !== 0 && targetPos < targetEnd)) break;
}
}
function payloadFile (pointer, cb) {
var target = Buffer.alloc(pointer[1]);
payloadCopyMany(pointer, target, 0, 0, function (error) {
if (error) return cb(error);
cb(null, target);
});
}
function payloadFileSync (pointer) {
var target = Buffer.alloc(pointer[1]);
payloadCopyManySync(pointer, target, 0, 0);
return target;
}
// /////////////////////////////////////////////////////////////////
// SETUP PROCESS ///////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
(function () {
process.pkg = {};
process.versions.pkg = '%VERSION%';
process.pkg.mount = createMountpoint;
process.pkg.entrypoint = ENTRYPOINT;
process.pkg.defaultEntrypoint = DEFAULT_ENTRYPOINT;
}());
// /////////////////////////////////////////////////////////////////
// PATH.RESOLVE REPLACEMENT ////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
(function () {
var path = require('path');
process.pkg.path = {};
process.pkg.path.resolve = function () {
var args = cloneArgs(arguments);
args.unshift(path.dirname(ENTRYPOINT));
return path.resolve.apply(path, args); // eslint-disable-line prefer-spread
};
}());
// /////////////////////////////////////////////////////////////////
// PATCH FS ////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
(function () {
var fs = require('fs');
var ancestor = {};
ancestor.openSync = fs.openSync;
ancestor.open = fs.open;
ancestor.readSync = fs.readSync;
ancestor.read = fs.read;
ancestor.writeSync = fs.writeSync;
ancestor.write = fs.write;
ancestor.closeSync = fs.closeSync;
ancestor.close = fs.close;
ancestor.readFileSync = fs.readFileSync;
ancestor.readFile = fs.readFile;
// ancestor.writeFileSync = fs.writeFileSync; // based on openSync/writeSync/closeSync
// ancestor.writeFile = fs.writeFile; // based on open/write/close
ancestor.readdirSync = fs.readdirSync;
ancestor.readdir = fs.readdir;
ancestor.realpathSync = fs.realpathSync;
ancestor.realpath = fs.realpath;
ancestor.statSync = fs.statSync;
ancestor.stat = fs.stat;
ancestor.lstatSync = fs.lstatSync;
ancestor.lstat = fs.lstat;
ancestor.fstatSync = fs.fstatSync;
ancestor.fstat = fs.fstat;
ancestor.existsSync = fs.existsSync;
ancestor.exists = fs.exists;
ancestor.accessSync = fs.accessSync;
ancestor.access = fs.access;
var windows = process.platform === 'win32';
var docks = {};
var ENOTDIR = windows ? 4052 : 20;
var ENOENT = windows ? 4058 : 2;
var EISDIR = windows ? 4068 : 21;
function assertEncoding (encoding) {
if (encoding && !Buffer.isEncoding(encoding)) {
throw new Error('Unknown encoding: ' + encoding);
}
}
function maybeCallback (args) {
var cb = args[args.length - 1];
return typeof cb === 'function' ? cb : rethrow;
}
function error_ENOENT (fileOrDirectory, path) { // eslint-disable-line camelcase
var error = new Error(
fileOrDirectory + ' \'' + stripSnapshot(path) + '\' ' +
'was not included into executable at compilation stage. ' +
'Please recompile adding it as asset or script.'
);
error.errno = -ENOENT;
error.code = 'ENOENT';
error.path = path;
error.pkg = true;
return error;
}
function error_EISDIR (path) { // eslint-disable-line camelcase
var error = new Error(
'EISDIR: illegal operation on a directory, read'
);
error.errno = -EISDIR;
error.code = 'EISDIR';
error.path = path;
error.pkg = true;
return error;
}
function error_ENOTDIR (path) { // eslint-disable-line camelcase
var error = new Error(
'ENOTDIR: not a directory, scandir \'' + path + '\''
);
error.errno = -ENOTDIR;
error.code = 'ENOTDIR';
error.path = path;
error.pkg = true;
return error;
}
// ///////////////////////////////////////////////////////////////
// open //////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function openFromSnapshot (path_, cb) {
var cb2 = cb || rethrow;
var path = normalizePath(path_);
// console.log("openFromSnapshot", path);
var entity = VIRTUAL_FILESYSTEM[path];
if (!entity) return cb2(error_ENOENT('File or directory', path));
var dock = { path: path, entity: entity, position: 0 };
var nullDevice = windows ? '\\\\.\\NUL' : '/dev/null';
if (cb) {
ancestor.open.call(fs, nullDevice, 'r', function (error, fd) {
if (error) return cb(error);
docks[fd] = dock;
cb(null, fd);
});
} else {
var fd = ancestor.openSync.call(fs, nullDevice, 'r');
docks[fd] = dock;
return fd;
}
}
fs.openSync = function (path) {
if (!insideSnapshot(path)) {
return ancestor.openSync.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.openSync.apply(fs, translateNth(arguments, 0, path));
}
return openFromSnapshot(path);
};
fs.open = function (path) {
if (!insideSnapshot(path)) {
return ancestor.open.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.open.apply(fs, translateNth(arguments, 0, path));
}
var callback = dezalgo(maybeCallback(arguments));
openFromSnapshot(path, callback);
};
// ///////////////////////////////////////////////////////////////
// read //////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function readFromSnapshotSub (entityContent, dock, buffer, offset, length, position, cb) {
var p;
if ((position !== null) && (position !== undefined)) {
p = position;
} else {
p = dock.position;
}
if (cb) {
payloadCopyUni(entityContent, buffer, offset, p, p + length, function (error, bytesRead, buffer2) {
if (error) return cb(error);
dock.position = p + bytesRead;
cb(null, bytesRead, buffer2);
});
} else {
var bytesRead = payloadCopyUni(entityContent, buffer, offset, p, p + length);
dock.position = p + bytesRead;
return bytesRead;
}
}
function readFromSnapshot (fd, buffer, offset, length, position, cb) {
var cb2 = cb || rethrow;
if ((offset < 0) && (NODE_VERSION_MAJOR >= 10)) return cb2(new Error(
'The value of "offset" is out of range. It must be >= 0 && <= ' + buffer.length.toString() + '. Received ' + offset));
if (offset < 0) return cb2(new Error('Offset is out of bounds'));
if ((offset >= buffer.length) && (NODE_VERSION_MAJOR >= 6)) return cb2(null, 0);
if (offset >= buffer.length) return cb2(new Error('Offset is out of bounds'));
if ((offset + length > buffer.length) && (NODE_VERSION_MAJOR >= 10)) return cb2(new Error(
'The value of "length" is out of range. It must be >= 0 && <= ' + (buffer.length - offset).toString() + '. Received ' + length.toString()));
if (offset + length > buffer.length) return cb2(new Error('Length extends beyond buffer'));
var dock = docks[fd];
var entity = dock.entity;
var entityLinks = entity[STORE_LINKS];
if (entityLinks) return cb2(error_EISDIR(dock.path));
var entityContent = entity[STORE_CONTENT];
if (entityContent) return readFromSnapshotSub(entityContent, dock, buffer, offset, length, position, cb);
return cb2(new Error('UNEXPECTED-15'));
}
fs.readSync = function (fd, buffer, offset, length, position) {
if (!docks[fd]) {
return ancestor.readSync.apply(fs, arguments);
}
return readFromSnapshot(fd, buffer, offset, length, position);
};
fs.read = function (fd, buffer, offset, length, position) {
if (!docks[fd]) {
return ancestor.read.apply(fs, arguments);
}
var callback = dezalgo(maybeCallback(arguments));
readFromSnapshot(fd, buffer, offset, length, position, callback);
};
// ///////////////////////////////////////////////////////////////
// write /////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function writeToSnapshot (cb) {
var cb2 = cb || rethrow;
return cb2(new Error('Cannot write to packaged file'));
}
fs.writeSync = function (fd) {
if (!docks[fd]) {
return ancestor.writeSync.apply(fs, arguments);
}
return writeToSnapshot();
};
fs.write = function (fd) {
if (!docks[fd]) {
return ancestor.write.apply(fs, arguments);
}
var callback = dezalgo(maybeCallback(arguments));
writeToSnapshot(callback);
};
// ///////////////////////////////////////////////////////////////
// close /////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function closeFromSnapshot (fd, cb) {
delete docks[fd];
if (cb) {
ancestor.close.call(fs, fd, cb);
} else {
return ancestor.closeSync.call(fs, fd);
}
}
fs.closeSync = function (fd) {
if (!docks[fd]) {
return ancestor.closeSync.apply(fs, arguments);
}
return closeFromSnapshot(fd);
};
fs.close = function (fd) {
if (!docks[fd]) {
return ancestor.close.apply(fs, arguments);
}
var callback = dezalgo(maybeCallback(arguments));
closeFromSnapshot(fd, callback);
};
// ///////////////////////////////////////////////////////////////
// readFile //////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function readFileOptions (options, hasCallback) {
if (!options || (hasCallback && typeof options === 'function')) {
return { encoding: null, flag: 'r' };
} else if (typeof options === 'string') {
return { encoding: options, flag: 'r' };
} else if (typeof options === 'object') {
return options;
} else {
return null;
}
}
function readFileFromSnapshotSub (entityContent, cb) {
if (cb) {
payloadFile(entityContent, cb);
} else {
return payloadFileSync(entityContent);
}
}
function readFileFromSnapshot (path_, cb) {
var cb2 = cb || rethrow;
var path = normalizePath(path_);
// console.log("readFileFromSnapshot", path);
var entity = VIRTUAL_FILESYSTEM[path];
if (!entity) return cb2(error_ENOENT('File', path));
var entityLinks = entity[STORE_LINKS];
if (entityLinks) return cb2(error_EISDIR(path));
var entityContent = entity[STORE_CONTENT];
if (entityContent) return readFileFromSnapshotSub(entityContent, cb);
var entityBlob = entity[STORE_BLOB];
if (entityBlob) return cb2(null, Buffer.from('source-code-not-available'));
// why return empty buffer?
// otherwise this error will arise:
// Error: UNEXPECTED-20
// at readFileFromSnapshot (e:0)
// at Object.fs.readFileSync (e:0)
// at Object.Module._extensions..js (module.js:421:20)
// at Module.load (module.js:357:32)
// at Function.Module._load (module.js:314:12)
// at Function.Module.runMain (e:0)
// at startup (node.js:140:18)
// at node.js:1001:3
return cb2(new Error('UNEXPECTED-20'));
}
fs.readFileSync = function (path, options_) {
if (path === 'dirty-hack-for-testing-purposes') {
return path;
}
if (!insideSnapshot(path)) {
return ancestor.readFileSync.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.readFileSync.apply(fs, translateNth(arguments, 0, path));
}
var options = readFileOptions(options_, false);
if (!options) {
return ancestor.readFileSync.apply(fs, arguments);
}
var encoding = options.encoding;
assertEncoding(encoding);
var buffer = readFileFromSnapshot(path);
if (encoding) buffer = buffer.toString(encoding);
return buffer;
};
fs.readFile = function (path, options_) {
if (!insideSnapshot(path)) {
return ancestor.readFile.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.readFile.apply(fs, translateNth(arguments, 0, path));
}
var options = readFileOptions(options_, true);
if (!options) {
return ancestor.readFile.apply(fs, arguments);
}
var encoding = options.encoding;
assertEncoding(encoding);
var callback = dezalgo(maybeCallback(arguments));
readFileFromSnapshot(path, function (error, buffer) {
if (error) return callback(error);
if (encoding) buffer = buffer.toString(encoding);
callback(null, buffer);
});
};
// ///////////////////////////////////////////////////////////////
// writeFile /////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
// writeFileSync based on openSync/writeSync/closeSync
// writeFile based on open/write/close
// ///////////////////////////////////////////////////////////////
// readdir ///////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function readdirOptions (options, hasCallback) {
if (!options || (hasCallback && typeof options === 'function')) {
return { encoding: null };
} else if (typeof options === 'string') {
return { encoding: options };
} else if (typeof options === 'object') {
return options;
} else {
return null;
}
}
function Dirent (name, type) {
this.name = name;
this.type = type;
}
Dirent.prototype.isDirectory = function () {
return this.type === 2;
};
Dirent.prototype.isFile = function () {
return this.type === 1;
};
Dirent.prototype.isBlockDevice =
Dirent.prototype.isCharacterDevice =
Dirent.prototype.isSymbolicLink =
Dirent.prototype.isFIFO =
Dirent.prototype.isSocket = function () {
return false;
};
function getFileTypes (path_, entries) {
return entries.map(function (entry) {
var path = require('path').join(path_, entry);
var entity = VIRTUAL_FILESYSTEM[path];
if (entity[STORE_BLOB] || entity[STORE_CONTENT]) return new Dirent(entry, 1);
if (entity[STORE_LINKS]) return new Dirent(entry, 2);
throw new Error('UNEXPECTED-24');
});
}
function readdirRoot (path, cb) {
if (cb) {
ancestor.readdir(path, function (error, entries) {
if (error) return cb(error);
entries.push('snapshot');
cb(null, entries);
});
} else {
var entries = ancestor.readdirSync(path);
entries.push('snapshot');
return entries;
}
}
function readdirFromSnapshotSub (entityLinks, path, cb) {
if (cb) {
payloadFile(entityLinks, function (error, buffer) {
if (error) return cb(error);
cb(null, JSON.parse(buffer).concat(readdirMountpoints(path)));
});
} else {
var buffer = payloadFileSync(entityLinks);
return JSON.parse(buffer).concat(readdirMountpoints(path));
}
}
function readdirFromSnapshot (path_, isRoot, cb) {
var cb2 = cb || rethrow;
if (isRoot) return readdirRoot(path_, cb);
var path = normalizePath(path_);
// console.log("readdirFromSnapshot", path);
var entity = VIRTUAL_FILESYSTEM[path];
if (!entity) return cb2(error_ENOENT('Directory', path));
var entityBlob = entity[STORE_BLOB];
if (entityBlob) return cb2(error_ENOTDIR(path));
var entityContent = entity[STORE_CONTENT];
if (entityContent) return cb2(error_ENOTDIR(path));
var entityLinks = entity[STORE_LINKS];
if (entityLinks) return readdirFromSnapshotSub(entityLinks, path, cb);
return cb2(new Error('UNEXPECTED-25'));
}
fs.readdirSync = function (path, options_) {
var isRoot = isRootPath(path);
if (!insideSnapshot(path) && !isRoot) {
return ancestor.readdirSync.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.readdirSync.apply(fs, translateNth(arguments, 0, path));
}
var options = readdirOptions(options_, false);
if (!options) {
return ancestor.readdirSync.apply(fs, arguments);
}
var entries = readdirFromSnapshot(path, isRoot);
if (options.withFileTypes) entries = getFileTypes(path, entries);
return entries;
};
fs.readdir = function (path, options_) {
var isRoot = isRootPath(path);
if (!insideSnapshot(path) && !isRoot) {
return ancestor.readdir.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.readdir.apply(fs, translateNth(arguments, 0, path));
}
var options = readdirOptions(options_, true);
if (!options) {
return ancestor.readdir.apply(fs, arguments);
}
var callback = dezalgo(maybeCallback(arguments));
readdirFromSnapshot(path, isRoot, function (error, entries) {
if (error) return callback(error);
if (options.withFileTypes) entries = getFileTypes(path, entries);
callback(null, entries);
});
};
// ///////////////////////////////////////////////////////////////
// realpath //////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function realpathFromSnapshot (path_) {
var path = normalizePath(path_);
// console.log("realpathFromSnapshot", path);
return path;
}
fs.realpathSync = function (path) {
if (!insideSnapshot(path)) {
return ancestor.realpathSync.apply(fs, arguments);
}
if (insideMountpoint(path)) {
// app should not know real file name
}
return realpathFromSnapshot(path);
};
fs.realpath = function (path) {
if (!insideSnapshot(path)) {
return ancestor.realpath.apply(fs, arguments);
}
if (insideMountpoint(path)) {
// app should not know real file name
}
var callback = dezalgo(maybeCallback(arguments));
callback(null, realpathFromSnapshot(path));
};
// ///////////////////////////////////////////////////////////////
// stat //////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function restore (s) {
s.blksize = 4096;
s.blocks = 0;
s.dev = 0;
s.gid = 20;
s.ino = 0;
s.nlink = 0;
s.rdev = 0;
s.uid = 500;
s.atime = new Date(EXECSTAT.atime);
s.mtime = new Date(EXECSTAT.mtime);
s.ctime = new Date(EXECSTAT.ctime);
s.birthtime = new Date(EXECSTAT.birthtime);
s.atimeMs = EXECSTAT.atimeMs;
s.mtimeMs = EXECSTAT.mtimeMs;
s.ctimeMs = EXECSTAT.ctimeMs;
s.birthtimeMs = EXECSTAT.birthtimeMs;
var isFileValue = s.isFileValue;
var isDirectoryValue = s.isDirectoryValue;
delete s.isFileValue;
delete s.isDirectoryValue;
s.isFile = function () {
return isFileValue;
};
s.isDirectory = function () {
return isDirectoryValue;
};
s.isSymbolicLink = function () {
return false;
};
s.isFIFO = function () {
return false;
};
return s;
}
function findNativeAddonForStat (path, cb) {
var cb2 = cb || rethrow;
var foundPath = findNativeAddonSyncUnderRequire(path);
if (!foundPath) return cb2(error_ENOENT('File or directory', path));
if (cb) {
ancestor.stat.call(fs, foundPath, cb);
} else {
return ancestor.statSync.call(fs, foundPath);
}
}
function statFromSnapshotSub (entityStat, cb) {
if (cb) {
payloadFile(entityStat, function (error, buffer) {
if (error) return cb(error);
cb(null, restore(JSON.parse(buffer)));
});
} else {
var buffer = payloadFileSync(entityStat);
return restore(JSON.parse(buffer));
}
}
function statFromSnapshot (path_, cb) {
var cb2 = cb || rethrow;
var path = normalizePath(path_);
// console.log("statFromSnapshot", path);
var entity = VIRTUAL_FILESYSTEM[path];
if (!entity) return findNativeAddonForStat(path, cb);
var entityStat = entity[STORE_STAT];
if (entityStat) return statFromSnapshotSub(entityStat, cb);
return cb2(new Error('UNEXPECTED-35'));
}
fs.statSync = function (path) {
if (!insideSnapshot(path)) {
return ancestor.statSync.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.statSync.apply(fs, translateNth(arguments, 0, path));
}
return statFromSnapshot(path);
};
fs.stat = function (path) {
if (!insideSnapshot(path)) {
return ancestor.stat.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.stat.apply(fs, translateNth(arguments, 0, path));
}
var callback = dezalgo(maybeCallback(arguments));
statFromSnapshot(path, callback);
};
// ///////////////////////////////////////////////////////////////
// lstat /////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
fs.lstatSync = function (path) {
if (!insideSnapshot(path)) {
return ancestor.lstatSync.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.lstatSync.apply(fs, translateNth(arguments, 0, path));
}
return statFromSnapshot(path);
};
fs.lstat = function (path) {
if (!insideSnapshot(path)) {
return ancestor.lstat.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.lstat.apply(fs, translateNth(arguments, 0, path));
}
var callback = dezalgo(maybeCallback(arguments));
statFromSnapshot(path, callback);
};
// ///////////////////////////////////////////////////////////////
// fstat /////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function fstatFromSnapshot (fd, cb) {
var cb2 = cb || rethrow;
var entity = docks[fd].entity;
var entityStat = entity[STORE_STAT];
if (entityStat) return statFromSnapshotSub(entityStat, cb);
return cb2(new Error('UNEXPECTED-40'));
}
fs.fstatSync = function (fd) {
if (!docks[fd]) {
return ancestor.fstatSync.apply(fs, arguments);
}
return fstatFromSnapshot(fd);
};
fs.fstat = function (fd) {
if (!docks[fd]) {
return ancestor.fstat.apply(fs, arguments);
}
var callback = dezalgo(maybeCallback(arguments));
fstatFromSnapshot(fd, callback);
};
// ///////////////////////////////////////////////////////////////
// exists ////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function findNativeAddonForExists (path) {
var foundPath = findNativeAddonSyncFreeFromRequire(path);
if (!foundPath) return false;
return ancestor.existsSync.call(fs, foundPath);
}
function existsFromSnapshot (path_) {
var path = normalizePath(path_);
// console.log("existsFromSnapshot", path);
var entity = VIRTUAL_FILESYSTEM[path];
if (!entity) return findNativeAddonForExists(path);
return true;
}
fs.existsSync = function (path) {
if (!insideSnapshot(path)) {
return ancestor.existsSync.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.existsSync.apply(fs, translateNth(arguments, 0, path));
}
return existsFromSnapshot(path);
};
fs.exists = function (path) {
if (!insideSnapshot(path)) {
return ancestor.exists.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.exists.apply(fs, translateNth(arguments, 0, path));
}
var callback = dezalgo(maybeCallback(arguments));
callback(existsFromSnapshot(path));
};
// ///////////////////////////////////////////////////////////////
// access ////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function accessFromSnapshot (path_, cb) {
var cb2 = cb || rethrow;
var path = normalizePath(path_);
// console.log("accessFromSnapshot", path);
var entity = VIRTUAL_FILESYSTEM[path];
if (!entity) return cb2(error_ENOENT('File or directory', path));
return cb2(null, undefined);
}
fs.accessSync = function (path) {
if (!insideSnapshot(path)) {
return ancestor.accessSync.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.accessSync.apply(fs, translateNth(arguments, 0, path));
}
return accessFromSnapshot(path);
};
fs.access = function (path) {
if (!insideSnapshot(path)) {
return ancestor.access.apply(fs, arguments);
}
if (insideMountpoint(path)) {
return ancestor.access.apply(fs, translateNth(arguments, 0, path));
}
var callback = dezalgo(maybeCallback(arguments));
accessFromSnapshot(path, callback);
};
// ///////////////////////////////////////////////////////////////
// INTERNAL //////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////
function makeLong (f) {
return require('path')._makeLong(f);
}
function revertMakingLong (f) {
if (/^\\\\\?\\/.test(f)) return f.slice(4);
return f;
}
function findNativeAddonForInternalModuleStat (path_) {
var path = findNativeAddonSyncUnderRequire(path_);
if (!path) return -ENOENT;
return process.binding('fs').internalModuleStat(makeLong(path));
}
fs.internalModuleStat = function (long) {
// from node comments:
// Used to speed up module loading. Returns 0 if the path refers to
// a file, 1 when it's a directory or < 0 on error (usually -ENOENT).
// The speedup comes from not creating thousands of Stat and Error objects.
var path = revertMakingLong(long);
if (!insideSnapshot(path)) {
return process.binding('fs').internalModuleStat(long);
}
if (insideMountpoint(path)) {
return process.binding('fs').internalModuleStat(makeLong(translate(path)));
}
path = normalizePath(path);
// console.log("internalModuleStat", path);
var entity = VIRTUAL_FILESYSTEM[path];
if (!entity) return findNativeAddonForInternalModuleStat(path);
var entityBlob = entity[STORE_BLOB];
if (entityBlob) return 0;
var entityContent = entity[STORE_CONTENT];
if (entityContent) return 0;
var entityLinks = entity[STORE_LINKS];
if (entityLinks) return 1;
return -ENOENT;
};
fs.internalModuleReadFile = fs.internalModuleReadJSON = function (long) {
// from node comments:
// Used to speed up module loading. Returns the contents of the file as
// a string or undefined when the file cannot be opened. The speedup
// comes from not creating Error objects on failure.
var path = revertMakingLong(long);
var bindingFs = process.binding('fs');
var readFile = (bindingFs.internalModuleReadFile ||
bindingFs.internalModuleReadJSON).bind(bindingFs);
if (!insideSnapshot(path)) {
return readFile(long);
}
if (insideMountpoint(path)) {
return readFile(makeLong(translate(path)));
}
path = normalizePath(path);
// console.log("internalModuleReadFile", path);
var entity = VIRTUAL_FILESYSTEM[path];
if (!entity) return undefined;
var entityContent = entity[STORE_CONTENT];
if (!entityContent) return undefined;
return payloadFileSync(entityContent).toString();
};
}());
// /////////////////////////////////////////////////////////////////
// PATCH MODULE ////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
(function () {
var Module = require('module');
var ancestor = {};
ancestor.require = Module.prototype.require;
ancestor._compile = Module.prototype._compile;
ancestor._resolveFilename = Module._resolveFilename;
ancestor.runMain = Module.runMain;
Module.prototype.require = function (path) {
try {
return ancestor.require.apply(this, arguments);
} catch (error) {
if (((error.code === 'ENOENT') ||
(error.code === 'MODULE_NOT_FOUND')) &&
(!insideSnapshot(path)) &&
(!require('path').isAbsolute(path))) {
if (!error.pkg) {
error.pkg = true;
error.message += '\n' +
'1) If you want to compile the package/file into ' +
'executable, please pay attention to compilation ' +
'warnings and specify a literal in \'require\' call. ' +
'2) If you don\'t want to compile the package/file ' +
'into executable and want to \'require\' it from ' +
'filesystem (likely plugin), specify an absolute ' +
'path in \'require\' call using process.cwd() or ' +
'process.execPath.';
}
}
throw error;
}
};
var im, makeRequireFunction;
if (NODE_VERSION_MAJOR === 0) {
makeRequireFunction = function (self) {
function rqfn (path) {
return self.require(path);
}
rqfn.resolve = function (request) {
return Module._resolveFilename(request, self);
};
rqfn.main = process.mainModule;
rqfn.extensions = Module._extensions;
rqfn.cache = Module._cache;
return rqfn;
};
} else
if (NODE_VERSION_MAJOR <= 9) {
im = require('internal/module');
if (NODE_VERSION_MAJOR <= 7) {
makeRequireFunction = function (m) {
return im.makeRequireFunction.call(m);
};
} else {
makeRequireFunction = im.makeRequireFunction;
}
} else {
im = require('internal/modules/cjs/helpers');
makeRequireFunction = im.makeRequireFunction;
// TODO esm modules along with cjs
}
Module.prototype._compile = function (content, filename_) {
if (!insideSnapshot(filename_)) {
return ancestor._compile.apply(this, arguments);
}
if (insideMountpoint(filename_)) {
// DONT TRANSLATE! otherwise __dirname gets real name
return ancestor._compile.apply(this, arguments);
}
var filename = normalizePath(filename_);
// console.log("_compile", filename);
var entity = VIRTUAL_FILESYSTEM[filename];
if (!entity) {
// let user try to "_compile" a packaged file
return ancestor._compile.apply(this, arguments);
}
var entityBlob = entity[STORE_BLOB];
var entityContent = entity[STORE_CONTENT];
if (entityBlob) {
var options = {
filename: filename,
lineOffset: 0,
displayErrors: true,
cachedData: payloadFileSync(entityBlob),
sourceless: !entityContent
};
var Script = require('vm').Script;
var code = entityContent
? require('module').wrap(payloadFileSync(entityContent))
: undefined;
var script = new Script(code, options);
var wrapper = script.runInThisContext(options);
if (!wrapper) process.exit(4); // for example VERSION_MISMATCH
var dirname = require('path').dirname(filename);
var rqfn = makeRequireFunction(this);
var args = [ this.exports, rqfn, this, filename, dirname ];
return wrapper.apply(this.exports, args);
}
if (entityContent) {
if (entityBlob) throw new Error('UNEXPECTED-50');
// content is already in utf8 and without BOM (that is expected
// by stock _compile), but entityContent is still a Buffer
return ancestor._compile.apply(this, arguments);
}
throw new Error('UNEXPECTED-55');
};
Module._resolveFilename = function () {
var filename;
var flagWasOn = false;
try {
filename = ancestor._resolveFilename.apply(this, arguments);
} catch (error) {
if (error.code !== 'MODULE_NOT_FOUND') throw error;
FLAG_ENABLE_PROJECT = true;
var savePathCache = Module._pathCache;
Module._pathCache = Object.create(null);
try {
filename = ancestor._resolveFilename.apply(this, arguments);
flagWasOn = true;
} finally {
Module._pathCache = savePathCache;
FLAG_ENABLE_PROJECT = false;
}
}
if (!insideSnapshot(filename)) {
return filename;
}
if (insideMountpoint(filename)) {
return filename;
}
if (flagWasOn) {
FLAG_ENABLE_PROJECT = true;
try {
var found = findNativeAddonSyncUnderRequire(filename);
if (found) filename = found;
} finally {
FLAG_ENABLE_PROJECT = false;
}
}
return filename;
};
Module.runMain = function () {
Module._load(ENTRYPOINT, null, true);
process._tickCallback();
};
}());
// /////////////////////////////////////////////////////////////////
// PATCH CHILD_PROCESS /////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
(function () {
var childProcess = require('child_process');
var ancestor = {};
ancestor.spawn = childProcess.spawn;
ancestor.spawnSync = childProcess.spawnSync;
ancestor.execFile = childProcess.execFile;
ancestor.execFileSync = childProcess.execFileSync;
ancestor.exec = childProcess.exec;
ancestor.execSync = childProcess.execSync;
function setOptsEnv (args) {
var pos = args.length - 1;
if (typeof args[pos] === 'function') pos -= 1;
if (typeof args[pos] !== 'object' || Array.isArray(args[pos])) {
pos += 1;
args.splice(pos, 0, {});
}
var opts = args[pos];
if (!opts.env) opts.env = require('util')._extend({}, process.env);
if (opts.env.PKG_EXECPATH === 'PKG_INVOKE_NODEJS') return;
opts.env.PKG_EXECPATH = EXECPATH;
}
function startsWith2 (args, index, name, impostor) {
var qsName = '"' + name + ' ';
if (args[index].slice(0, qsName.length) === qsName) {
args[index] = '"' + impostor + ' ' + args[index].slice(qsName.length);
return true;
}
var sName = name + ' ';
if (args[index].slice(0, sName.length) === sName) {
args[index] = impostor + ' ' + args[index].slice(sName.length);
return true;
}
if (args[index] === name) {
args[index] = impostor;
return true;
}
return false;
}
function startsWith (args, index, name) {
var qName = '"' + name + '"';
var qEXECPATH = '"' + EXECPATH + '"';
var jsName = JSON.stringify(name);
var jsEXECPATH = JSON.stringify(EXECPATH);
return startsWith2(args, index, name, EXECPATH) ||
startsWith2(args, index, qName, qEXECPATH) ||
startsWith2(args, index, jsName, jsEXECPATH);
}
function modifyLong (args, index) {
if (!args[index]) return;
return (startsWith(args, index, 'node') ||
startsWith(args, index, ARGV0) ||
startsWith(args, index, ENTRYPOINT) ||
startsWith(args, index, EXECPATH));
}
function modifyShort (args) {
if (!args[0]) return;
if (!Array.isArray(args[1])) {
args.splice(1, 0, []);
}
if (args[0] === 'node' ||
args[0] === ARGV0 ||
args[0] === ENTRYPOINT ||
args[0] === EXECPATH) {
args[0] = EXECPATH;
if (NODE_VERSION_MAJOR === 0) {
args[1] = args[1].filter(function (a) {
return (a.slice(0, 13) !== '--debug-port=');
});
}
} else {
for (var i = 1; i < args[1].length; i += 1) {
var mbc = args[1][i - 1];
if (mbc === '-c' || mbc === '/c') {
modifyLong(args[1], i);
}
}
}
}
childProcess.spawn = function () {
var args = cloneArgs(arguments);
setOptsEnv(args);
modifyShort(args);
return ancestor.spawn.apply(childProcess, args);
};
childProcess.spawnSync = function () {
var args = cloneArgs(arguments);
setOptsEnv(args);
modifyShort(args);
return ancestor.spawnSync.apply(childProcess, args);
};
childProcess.execFile = function () {
var args = cloneArgs(arguments);
setOptsEnv(args);
modifyShort(args);
return ancestor.execFile.apply(childProcess, args);
};
childProcess.execFileSync = function () {
var args = cloneArgs(arguments);
setOptsEnv(args);
modifyShort(args);
return ancestor.execFileSync.apply(childProcess, args);
};
childProcess.exec = function () {
var args = cloneArgs(arguments);
setOptsEnv(args);
modifyLong(args, 0);
return ancestor.exec.apply(childProcess, args);
};
childProcess.execSync = function () {
var args = cloneArgs(arguments);
setOptsEnv(args);
modifyLong(args, 0);
return ancestor.execSync.apply(childProcess, args);
};
}());
// /////////////////////////////////////////////////////////////////
// PROMISIFY ///////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////
(function () {
var util = require('util');
var promisify = util.promisify;
if (promisify) {
var custom = promisify.custom;
var customPromisifyArgs = require('internal/util').customPromisifyArgs;
// /////////////////////////////////////////////////////////////
// FS //////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////
Object.defineProperty(require('fs').exists, custom, {
value: function (path) {
return new Promise(function (resolve) {
require('fs').exists(path, function (exists) {
resolve(exists);
});
});
}
});
Object.defineProperty(require('fs').read, customPromisifyArgs, {
value: [ 'bytesRead', 'buffer' ]
});
Object.defineProperty(require('fs').write, customPromisifyArgs, {
value: [ 'bytesWritten', 'buffer' ]
});
// /////////////////////////////////////////////////////////////
// CHILD_PROCESS ///////////////////////////////////////////////
// /////////////////////////////////////////////////////////////
var customPromiseExecFunction = function (o) {
return function () {
var args = Array.from(arguments);
return new Promise(function (resolve, reject) {
o.apply(undefined, args.concat(function (error, stdout, stderr) {
if (error !== null) {
error.stdout = stdout;
error.stderr = stderr;
reject(error);
} else {
resolve({ stdout: stdout, stderr: stderr });
}
}));
});
};
};
Object.defineProperty(require('child_process').exec, custom, {
value: customPromiseExecFunction(require('child_process').exec)
});
Object.defineProperty(require('child_process').execFile, custom, {
value: customPromiseExecFunction(require('child_process').execFile)
});
}
}());