Created
December 22, 2019 23:17
-
-
Save FlamingTempura/0ddfd3f48576da23fa1226f6f6a76105 to your computer and use it in GitHub Desktop.
Making a binary from Node.js scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { spawn } = require('child_process'); | |
const path = require('path'); | |
const fs = require('fs'); | |
const ROOT_DIR = __dirname; | |
const OUTPUT_DIR = path.join(__dirname, 'bin'); // where the binary should end up | |
const OUTPUT = path.join(OUTPUT_DIR, 'my_program'); // name of the binary | |
const BUILD_DIR = path.join(__dirname, 'build'); // location for temporary build files | |
const MAIN = 'your-code.js'; // the entrypoint to your code | |
const INCLUDE = [ | |
process.execPath, // copy node binary | |
'package.json', | |
'node_modules', | |
MAIN, | |
'something.jpg', // files/directories you wish to include | |
'something-else.js', | |
'some-directory' | |
]; | |
const PAYLOAD_TAR_GZ = path.join(ROOT_DIR, 'payload.tar.gz'); | |
const SH_TEMPLATE = [ | |
'#!/bin/sh', | |
'export TMPDIR=`mktemp -d /tmp/nodescript.XXXXXX`', // create temp install dir | |
'function __cleanup () {', | |
'rm -rf $TMPDIR', // when finish, remove temp install dir | |
'}', | |
'trap __cleanup EXIT', | |
'ARCHIVE=`awk \'/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }\' $0`',// get line of archive start | |
'tail -n+$ARCHIVE $0 | tar xz -C $TMPDIR', // extact archive to temp install dir | |
//'cd $TMPDIR', // go to temp install dir | |
`$TMPDIR/node $TMPDIR/${MAIN}`, // run the program | |
'__cleanup', // when finish, remove temp install dir | |
'exit 0', // exit before getting to the included file | |
'__ARCHIVE_BELOW__' // insert marker | |
].join('\n'); | |
const exec = async (cmd, args, cwd = ROOT_DIR) => new Promise((resolve, reject) => { | |
console.log(['>', cmd, ...args].join(' ')); | |
const p = spawn(cmd, [...args], { stdio: 'inherit', cwd }); | |
p.on('error', e => reject(e)); | |
p.on('close', code => resolve(code)); | |
}); | |
const build = async () => { | |
await exec('rm', ['-rf', BUILD_DIR]); | |
await exec('mkdir', ['-p', BUILD_DIR]); | |
await exec('mkdir', ['-p', OUTPUT_DIR]); | |
await exec('cp', ['-r', ...INCLUDE, BUILD_DIR]); // copy all needed files to the build directory | |
await exec('npm', ['prune', '--production'], BUILD_DIR); // remove unneeded modules (e.g. devDependencies) | |
await exec('tar', [ // create the tar.gz which will be appended to the binary | |
'-c', // create | |
'-z', // gzip | |
'-C', BUILD_DIR, // working directory | |
'-f', PAYLOAD_TAR_GZ, // output | |
'--overwrite', // don't ask before overwrite | |
'.' // contents | |
]); | |
await new Promise(resolve => { // write the executable | |
const ws = fs.createWriteStream(OUTPUT, { encoding: 'ascii' }); | |
ws.write(SH_TEMPLATE + '\n'); | |
fs.createReadStream(PAYLOAD_TAR_GZ).pipe(ws); | |
ws.on('close', resolve); | |
}); | |
await exec('chmod', ['+x', OUTPUT]); // make it executable | |
await exec('rm', ['-rf', BUILD_DIR, PAYLOAD_TAR_GZ]); // remove build files | |
}; | |
build(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment