2025-04-02 06:50:39 -04:00

479 lines
15 KiB
JavaScript

'use strict';
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, '__esModule', { value: true });
const zlib_1 = require('zlib');
const multistream_1 = __importDefault(require('multistream'));
const assert_1 = __importDefault(require('assert'));
const child_process_1 = require('child_process');
const fs_extra_1 = __importDefault(require('fs-extra'));
const into_stream_1 = __importDefault(require('into-stream'));
const path_1 = __importDefault(require('path'));
const stream_meter_1 = __importDefault(require('stream-meter'));
const common_1 = require('./common');
const log_1 = require('./log');
const fabricator_1 = require('./fabricator');
const types_1 = require('./types');
const compress_type_1 = require('./compress_type');
function discoverPlaceholder(binaryBuffer, searchString, padder) {
const placeholder = Buffer.from(searchString);
const position = binaryBuffer.indexOf(placeholder);
if (position === -1) {
return { notFound: true };
}
return { position, size: placeholder.length, padder };
}
function injectPlaceholder(fd, placeholder, value, cb) {
if ('notFound' in placeholder) {
(0, assert_1.default)(false, 'Placeholder for not found');
}
const { position, size, padder } = placeholder;
let stringValue = Buffer.from('');
if (typeof value === 'number') {
stringValue = Buffer.from(value.toString());
} else if (typeof value === 'string') {
stringValue = Buffer.from(value);
} else {
stringValue = value;
}
const padding = Buffer.from(padder.repeat(size - stringValue.length));
stringValue = Buffer.concat([stringValue, padding]);
fs_extra_1.default.write(
fd,
stringValue,
0,
stringValue.length,
position,
cb
);
}
function discoverPlaceholders(binaryBuffer) {
return {
BAKERY: discoverPlaceholder(
binaryBuffer,
`\0${'// BAKERY '.repeat(20)}`,
'\0'
),
PAYLOAD_POSITION: discoverPlaceholder(
binaryBuffer,
'// PAYLOAD_POSITION //',
' '
),
PAYLOAD_SIZE: discoverPlaceholder(binaryBuffer, '// PAYLOAD_SIZE //', ' '),
PRELUDE_POSITION: discoverPlaceholder(
binaryBuffer,
'// PRELUDE_POSITION //',
' '
),
PRELUDE_SIZE: discoverPlaceholder(binaryBuffer, '// PRELUDE_SIZE //', ' '),
};
}
function injectPlaceholders(fd, placeholders, values, cb) {
injectPlaceholder(fd, placeholders.BAKERY, values.BAKERY, (error) => {
if (error) {
return cb(error);
}
injectPlaceholder(
fd,
placeholders.PAYLOAD_POSITION,
values.PAYLOAD_POSITION,
(error2) => {
if (error2) {
return cb(error2);
}
injectPlaceholder(
fd,
placeholders.PAYLOAD_SIZE,
values.PAYLOAD_SIZE,
(error3) => {
if (error3) {
return cb(error3);
}
injectPlaceholder(
fd,
placeholders.PRELUDE_POSITION,
values.PRELUDE_POSITION,
(error4) => {
if (error4) {
return cb(error4);
}
injectPlaceholder(
fd,
placeholders.PRELUDE_SIZE,
values.PRELUDE_SIZE,
cb
);
}
);
}
);
}
);
});
}
function makeBakeryValueFromBakes(bakes) {
const parts = [];
if (bakes.length) {
for (let i = 0; i < bakes.length; i += 1) {
parts.push(Buffer.from(bakes[i]));
parts.push(Buffer.alloc(1));
}
parts.push(Buffer.alloc(1));
}
return Buffer.concat(parts);
}
function replaceDollarWise(s, sf, st) {
return s.replace(sf, () => st);
}
function makePreludeBufferFromPrelude(prelude) {
return Buffer.from(
`(function(process, require, console, EXECPATH_FD, PAYLOAD_POSITION, PAYLOAD_SIZE) { ${prelude}\n})` // dont remove \n
);
}
function findPackageJson(nodeFile) {
let dir = nodeFile;
while (dir !== '/') {
dir = path_1.default.dirname(dir);
if (
fs_extra_1.default.existsSync(path_1.default.join(dir, 'package.json'))
) {
break;
}
}
if (dir === '/') {
throw new Error(`package.json not found for "${nodeFile}"`);
}
return dir;
}
function nativePrebuildInstall(target, nodeFile) {
var _a, _b;
const prebuildInstall = path_1.default.join(
__dirname,
'../node_modules/.bin/prebuild-install'
);
const dir = findPackageJson(nodeFile);
// parse the target node version from the binaryPath
const nodeVersion = path_1.default.basename(target.binaryPath).split('-')[1];
if (!/^v[0-9]+\.[0-9]+\.[0-9]+$/.test(nodeVersion)) {
throw new Error(`Couldn't find node version, instead got: ${nodeVersion}`);
}
const nativeFile = `${nodeFile}.${target.platform}.${nodeVersion}`;
if (fs_extra_1.default.existsSync(nativeFile)) {
return nativeFile;
}
// prebuild-install will overwrite the target .node file, so take a backup
if (!fs_extra_1.default.existsSync(`${nodeFile}.bak`)) {
fs_extra_1.default.copyFileSync(nodeFile, `${nodeFile}.bak`);
}
const napiVersions =
(
(_b =
(
(_a = JSON.parse(
fs_extra_1.default.readFileSync(
path_1.default.join(dir, 'package.json'),
{ encoding: 'utf-8' }
)
)) === null || _a === void 0
) ?
void 0
: _a.binary) === null || _b === void 0
) ?
void 0
: _b.napi_versions;
const options = [
'--platform',
types_1.platform[target.platform],
'--arch',
target.arch,
];
if (napiVersions == null) {
// TODO: consider target node version and supported n-api version
options.push('--target', nodeVersion);
}
// run prebuild
(0, child_process_1.execFileSync)(prebuildInstall, options, { cwd: dir });
// move the prebuild to a new name with a platform/version extension
fs_extra_1.default.copyFileSync(nodeFile, nativeFile);
// put the backed up file back
fs_extra_1.default.moveSync(`${nodeFile}.bak`, nodeFile, { overwrite: true });
return nativeFile;
}
/**
* instead of creating a vfs dicionnary with actual path as key
* we use a compression mechanism that can reduce significantly
* the memory footprint of the vfs in the code.
*
* without vfs compression:
*
* vfs = {
* "/folder1/folder2/file1.js": {};
* "/folder1/folder2/folder3/file2.js": {};
* "/folder1/folder2/folder3/file3.js": {};
* }
*
* with compression :
*
* fileDictionary = {
* "folder1": "1",
* "folder2": "2",
* "file1": "3",
* "folder3": "4",
* "file2": "5",
* "file3": "6",
* }
* vfs = {
* "/1/2/3": {};
* "/1/2/4/5": {};
* "/1/2/4/6": {};
* }
*
* note: the key is computed in base36 for further compression.
*/
const fileDictionary = {};
let counter = 0;
function getOrCreateHash(fileOrFolderName) {
let existingKey = fileDictionary[fileOrFolderName];
if (!existingKey) {
const newkey = counter;
counter += 1;
existingKey = newkey.toString(36);
fileDictionary[fileOrFolderName] = existingKey;
}
return existingKey;
}
const separator = '/';
function makeKey(doCompression, fullpath, slash) {
if (doCompression === compress_type_1.CompressType.None) return fullpath;
return fullpath.split(slash).map(getOrCreateHash).join(separator);
}
function producer({
backpack,
bakes,
slash,
target,
symLinks,
doCompress,
nativeBuild,
}) {
return new Promise((resolve, reject) => {
if (!Buffer.alloc) {
throw (0, log_1.wasReported)(
'Your node.js does not have Buffer.alloc. Please upgrade!'
);
}
const { prelude } = backpack;
let { entrypoint, stripes } = backpack;
entrypoint = (0, common_1.snapshotify)(entrypoint, slash);
stripes = stripes.slice();
const vfs = {};
for (const stripe of stripes) {
let { snap } = stripe;
snap = (0, common_1.snapshotify)(snap, slash);
const vfsKey = makeKey(doCompress, snap, slash);
if (!vfs[vfsKey]) vfs[vfsKey] = {};
}
const snapshotSymLinks = {};
for (const [key, value] of Object.entries(symLinks)) {
const k = (0, common_1.snapshotify)(key, slash);
const v = (0, common_1.snapshotify)(value, slash);
const vfsKey = makeKey(doCompress, k, slash);
snapshotSymLinks[vfsKey] = makeKey(doCompress, v, slash);
}
let meter;
let count = 0;
function pipeToNewMeter(s) {
meter = (0, stream_meter_1.default)();
return s.pipe(meter);
}
function pipeMayCompressToNewMeter(s) {
if (doCompress === compress_type_1.CompressType.GZip) {
return pipeToNewMeter(s.pipe((0, zlib_1.createGzip)()));
}
if (doCompress === compress_type_1.CompressType.Brotli) {
return pipeToNewMeter(s.pipe((0, zlib_1.createBrotliCompress)()));
}
return pipeToNewMeter(s);
}
function next(s) {
count += 1;
return pipeToNewMeter(s);
}
const binaryBuffer = fs_extra_1.default.readFileSync(target.binaryPath);
const placeholders = discoverPlaceholders(binaryBuffer);
let track = 0;
let prevStripe;
let payloadPosition;
let payloadSize;
let preludePosition;
let preludeSize;
new multistream_1.default((cb) => {
if (count === 0) {
return cb(null, next((0, into_stream_1.default)(binaryBuffer)));
}
if (count === 1) {
payloadPosition = meter.bytes;
return cb(null, next((0, into_stream_1.default)(Buffer.alloc(0))));
}
if (count === 2) {
if (prevStripe && !prevStripe.skip) {
const { store } = prevStripe;
let { snap } = prevStripe;
snap = (0, common_1.snapshotify)(snap, slash);
const vfsKey = makeKey(doCompress, snap, slash);
vfs[vfsKey][store] = [track, meter.bytes];
track += meter.bytes;
}
if (stripes.length) {
// clone to prevent 'skip' propagate
// to other targets, since same stripe
// is used for several targets
const stripe = Object.assign({}, stripes.shift());
prevStripe = stripe;
if (stripe.buffer) {
if (stripe.store === common_1.STORE_BLOB) {
const snap = (0, common_1.snapshotify)(stripe.snap, slash);
return (0, fabricator_1.fabricateTwice)(
bakes,
target.fabricator,
snap,
stripe.buffer,
(error, buffer) => {
if (error) {
log_1.log.warn(error.message);
stripe.skip = true;
return cb(
null,
(0, into_stream_1.default)(Buffer.alloc(0))
);
}
cb(
null,
pipeMayCompressToNewMeter(
(0, into_stream_1.default)(buffer || Buffer.from(''))
)
);
}
);
}
return cb(
null,
pipeMayCompressToNewMeter(
(0, into_stream_1.default)(stripe.buffer)
)
);
}
if (stripe.file) {
if (stripe.file === target.output) {
return cb(
(0, log_1.wasReported)(
'Trying to take executable into executable',
stripe.file
),
null
);
}
assert_1.default.strictEqual(stripe.store, common_1.STORE_CONTENT); // others must be buffers from walker
if ((0, common_1.isDotNODE)(stripe.file) && nativeBuild) {
try {
const platformFile = nativePrebuildInstall(target, stripe.file);
if (fs_extra_1.default.existsSync(platformFile)) {
return cb(
null,
pipeMayCompressToNewMeter(
fs_extra_1.default.createReadStream(platformFile)
)
);
}
} catch (err) {
log_1.log.debug(
`prebuild-install failed[${stripe.file}]:`,
err.message
);
}
}
return cb(
null,
pipeMayCompressToNewMeter(
fs_extra_1.default.createReadStream(stripe.file)
)
);
}
(0, assert_1.default)(false, 'producer: bad stripe');
} else {
payloadSize = track;
preludePosition = payloadPosition + payloadSize;
return cb(
null,
next(
(0, into_stream_1.default)(
makePreludeBufferFromPrelude(
replaceDollarWise(
replaceDollarWise(
replaceDollarWise(
replaceDollarWise(
replaceDollarWise(
prelude,
'%VIRTUAL_FILESYSTEM%',
JSON.stringify(vfs)
),
'%DEFAULT_ENTRYPOINT%',
JSON.stringify(entrypoint)
),
'%SYMLINKS%',
JSON.stringify(snapshotSymLinks)
),
'%DICT%',
JSON.stringify(fileDictionary)
),
'%DOCOMPRESS%',
JSON.stringify(doCompress)
)
)
)
)
);
}
} else {
return cb(null, null);
}
})
.on('error', (error) => {
reject(error);
})
.pipe(fs_extra_1.default.createWriteStream(target.output))
.on('error', (error) => {
reject(error);
})
.on('close', () => {
preludeSize = meter.bytes;
fs_extra_1.default.open(target.output, 'r+', (error, fd) => {
if (error) return reject(error);
injectPlaceholders(
fd,
placeholders,
{
BAKERY: makeBakeryValueFromBakes(bakes),
PAYLOAD_POSITION: payloadPosition,
PAYLOAD_SIZE: payloadSize,
PRELUDE_POSITION: preludePosition,
PRELUDE_SIZE: preludeSize,
},
(error2) => {
if (error2) return reject(error2);
fs_extra_1.default.close(fd, (error3) => {
if (error3) return reject(error3);
resolve();
});
}
);
});
});
});
}
exports.default = producer;
//# sourceMappingURL=producer.js.map