nexe
Version:
Create a single executable out of your Node.js application
150 lines (149 loc) • 5.57 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Bundle = exports.toStream = void 0;
const fs = require("fs");
const util_1 = require("util");
const path_1 = require("path");
const stream_1 = require("stream");
const MultiStream = require("multistream");
const lstat = (0, util_1.promisify)(fs.lstat), realpath = (0, util_1.promisify)(fs.realpath), createReadStream = fs.createReadStream, stat = (0, util_1.promisify)(fs.stat);
function makeRelative(cwd, path) {
return './' + (0, path_1.relative)(cwd, path);
}
function sortEntry(a, b) {
return a[0] > b[0] ? 1 : -1;
}
function toStream(content) {
const readable = new stream_1.Readable({
read() {
this.push(content);
this.push(null);
},
});
return readable;
}
exports.toStream = toStream;
function createFile(absoluteFileName) {
return __awaiter(this, void 0, void 0, function* () {
const stats = yield lstat(absoluteFileName), file = {
size: stats.size,
moduleType: 'commonjs',
contents: '',
absPath: absoluteFileName,
deps: {},
};
if (stats.isSymbolicLink()) {
file.size = stats.size;
const [realPath, realStat] = yield Promise.all([
realpath(absoluteFileName),
stat(absoluteFileName),
]);
file.realPath = realPath;
file.realSize = realStat.size;
}
return file;
});
}
class Bundle {
constructor({ cwd } = { cwd: process.cwd() }) {
this.size = 0;
this.rendered = false;
this.offset = 0;
this.index = {};
this.files = {};
this.streams = [];
this.cwd = cwd;
}
get list() {
return Object.keys(this.files);
}
addResource(absoluteFileName, content) {
return __awaiter(this, void 0, void 0, function* () {
if (this.files[absoluteFileName]) {
return this.size;
}
if (typeof content === 'string' || Buffer.isBuffer(content)) {
this.files[absoluteFileName] = {
size: Buffer.byteLength(content),
moduleType: 'commonjs',
contents: content,
deps: {},
absPath: absoluteFileName,
};
}
else if (content) {
this.files[content.absPath] = content;
}
else {
this.files[absoluteFileName] = {
absPath: absoluteFileName,
moduleType: 'commonjs',
contents: '',
deps: {},
size: 0,
};
this.files[absoluteFileName] = yield createFile(absoluteFileName);
}
return (this.size += this.files[absoluteFileName].size);
});
}
/**
* De-dupe files by absolute path, partition by symlink/real
* Iterate over real, add entries
* Iterate over symlinks, add symlinks
*/
renderIndex() {
if (this.rendered) {
throw new Error('Bundle index already rendered');
}
const files = Object.entries(this.files), realFiles = [], symLinks = [];
for (const entry of files) {
if (entry[1].realPath) {
symLinks.push(entry);
}
else {
realFiles.push(entry);
}
}
realFiles.sort(sortEntry);
symLinks.sort(sortEntry);
for (const [absPath, file] of realFiles) {
this.addEntry(absPath, file);
}
for (const [absPath, file] of symLinks) {
this.addEntry(file.realPath, file);
this.addEntry(absPath, file, file.realPath);
}
this.rendered = true;
return this.index;
}
/**
* Add a stream if needed and an entry with the required offset and size
* Ensure the calling order of this method is idempotent (eg, while iterating a sorted set)
* @param entryPath
* @param file
* @param useEntry
*/
addEntry(entryPath, file, useEntry) {
var _a;
const existingName = useEntry && makeRelative(this.cwd, useEntry), name = makeRelative(this.cwd, entryPath), size = (_a = file.realSize) !== null && _a !== void 0 ? _a : file.size, existingEntry = this.index[existingName !== null && existingName !== void 0 ? existingName : name];
this.index[name] = existingEntry || [this.offset, size];
if (!existingEntry) {
this.streams.push(() => file.contents ? toStream(file.contents) : createReadStream(file.absPath));
this.offset += size;
}
}
toStream() {
return new MultiStream(this.streams);
}
}
exports.Bundle = Bundle;