nodejs / node

Node.js JavaScript runtime ✨🐢🚀✨
https://nodejs.org
Other
104.27k stars 28.06k forks source link

Programmatic SEA blob generation API #52777

Open dr-vortex opened 2 weeks ago

dr-vortex commented 2 weeks ago

Currently, CLI commands and many files are involved in generating a SEA using Javascript.

For example:

import { execSync } from 'node:child_process';
import { copyFileSync, readFileSync, writeFileSync } from 'node:fs';
import { inject } from 'postject';

const blobPath = 'path/to/sea.blob',
    configPath = 'path/to/sea.json',
    outputPath = 'path/to/executable';

writeFileSync(
    configPath,
    JSON.stringify({
        main: 'path/to/main.js',
        output: blobPath,
        disableExperimentalSEAWarning: true,
    })
);
execSync('node --experimental-sea-config ' + configPath, { stdio: 'inherit' });
copyFileSync(process.execPath, outputPath);
await inject(outputPath, 'NODE_SEA_BLOB', readFileSync(blobPath), {
    machoSegmentName: 'NODE_SEA',
    sentinelFuse: 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2',
});

It would be nice if Node.js provided a Javascript API for generating SEA blobs. This would eliminate extra file creation and streamline the SEA creation process. I propose adding a function to the node:sea module/API (Typescript used for clarity):

/**
 * Configuration options for generating a SEA blob
 * @see https://nodejs.org/api/single-executable-applications.html#generating-single-executable-preparation-blobs
 */
interface BlobConfiguration {
    main: string;
    disableExperimentalSEAWarning?: boolean;
    useSnapshot?: boolean;
    useCodeCache?: boolean;
    assets?: Record<string, string>;
}

/**
 * Generates a SEA blob to be injected into an executable
 * @param config configuration options for the blob
 * @returns The blob contents in a buffer
 */
function generateBlob(config: BlobConfiguration): Buffer;

This new function in the SEA API would allow for more streamlined and readable code:

import { copyFileSync } from 'node:fs';
import { generateBlob } from 'node:sea';
import { inject } from 'postject';

const outputPath = 'path/to/executable';

const blob = generateBlob({
    main: 'path/to/main.js',
    disableExperimentalSEAWarning: true,
});
copyFileSync(process.execPath, outputPath);
await inject(outputPath, 'NODE_SEA_BLOB', blob, {
    machoSegmentName: 'NODE_SEA',
    sentinelFuse: 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2',
});

Which could even be inlined:

import { copyFileSync } from 'node:fs';
import { generateBlob } from 'node:sea';
import { inject } from 'postject';

const outputPath = 'path/to/executable';

copyFileSync(process.execPath, outputPath);
await inject(
    outputPath,
    'NODE_SEA_BLOB',
    generateBlob({
        main: 'path/to/main.js',
        disableExperimentalSEAWarning: true,
    }),
    {
        machoSegmentName: 'NODE_SEA',
        sentinelFuse: 'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2',
    }
);

Discussion Issue on nodejs/single-executable