UNPKG

@bravemobile/react-native-code-push

Version:

React Native plugin for the CodePush service

179 lines (178 loc) 6.48 kB
/** * code based on appcenter-cli */ import childProcess from "child_process"; import fs from "fs"; import path from "path"; import shell from "shelljs"; /** * Run Hermes compile CLI command * * @param bundleName {string} JS bundle file name * @param outputPath {string} Path to output .hbc file * @param sourcemapOutput {string} Path to output sourcemap file (Warning: if sourcemapOutput points to the outputPath, the sourcemap will be included in the CodePush bundle and increase the deployment size) * @param extraHermesFlags {string[]} Additional options to pass to `hermesc` command * @return {Promise<void>} */ export async function runHermesEmitBinaryCommand(bundleName, outputPath, sourcemapOutput, extraHermesFlags = []) { const hermesArgs = [ '-emit-binary', '-out', path.join(outputPath, bundleName + '.hbc'), path.join(outputPath, bundleName), ...extraHermesFlags, ]; if (sourcemapOutput) { hermesArgs.push('-output-source-map'); } console.log('Converting JS bundle to byte code via Hermes, running command:\n'); return new Promise((resolve, reject) => { try { const hermesCommand = getHermesCommand(); const disableAllWarningsArg = '-w'; shell.exec(`${hermesCommand} ${hermesArgs.join(' ')} ${disableAllWarningsArg}`); // Copy HBC bundle to overwrite JS bundle const source = path.join(outputPath, bundleName + '.hbc'); const destination = path.join(outputPath, bundleName); shell.cp(source, destination); shell.rm(source); resolve(); } catch (e) { reject(e); } }).then(() => { if (!sourcemapOutput) { // skip source map compose if source map is not enabled return; } // compose-source-maps.js file path const composeSourceMapsPath = getComposeSourceMapsPath(); if (composeSourceMapsPath === null) { throw new Error('react-native compose-source-maps.js scripts is not found'); } const jsCompilerSourceMapFile = path.join(outputPath, bundleName + '.hbc' + '.map'); if (!fs.existsSync(jsCompilerSourceMapFile)) { throw new Error(`sourcemap file ${jsCompilerSourceMapFile} is not found`); } return new Promise((resolve, reject) => { const composeSourceMapsArgs = [ composeSourceMapsPath, sourcemapOutput, jsCompilerSourceMapFile, '-o', sourcemapOutput, ]; const composeSourceMapsProcess = childProcess.spawn('node', composeSourceMapsArgs); console.log(`${composeSourceMapsPath} ${composeSourceMapsArgs.join(' ')}`); composeSourceMapsProcess.stdout.on('data', (data) => { console.log(data.toString().trim()); }); composeSourceMapsProcess.stderr.on('data', (data) => { console.error(data.toString().trim()); }); composeSourceMapsProcess.on('close', (exitCode, signal) => { if (exitCode !== 0) { reject(new Error(`"compose-source-maps" command failed (exitCode=${exitCode}, signal=${signal}).`)); } // Delete the HBC sourceMap, otherwise it will be included in 'code-push' bundle as well fs.unlink(jsCompilerSourceMapFile, (err) => { if (err != null) { console.error(err); reject(err); } resolve(); }); }); }); }); } function getHermesCommand() { const fileExists = (file) => { try { return fs.statSync(file).isFile(); } catch (e) { return false; } }; const hermescExecutable = path.join(getHermesCompilerPath(), getHermesOSBin(), getHermesOSExe()); if (fileExists(hermescExecutable)) { return hermescExecutable; } throw new Error('Hermes engine binary not found. Please upgrade to react-native 0.69 or later'); } function getHermesOSBin() { switch (process.platform) { case 'win32': return 'win64-bin'; case 'darwin': return 'osx-bin'; case 'freebsd': case 'linux': case 'sunos': default: return 'linux64-bin'; } } function getHermesOSExe() { const hermesExecutableName = 'hermesc'; switch (process.platform) { case 'win32': return hermesExecutableName + '.exe'; default: return hermesExecutableName; } } function getComposeSourceMapsPath() { // detect if compose-source-maps.js script exists const composeSourceMaps = path.join(getReactNativePackagePath(), 'scripts', 'compose-source-maps.js'); if (fs.existsSync(composeSourceMaps)) { return composeSourceMaps; } return null; } function getReactNativePackagePath() { const result = childProcess.spawnSync('node', [ '--print', "require.resolve('react-native/package.json')", ]); const packagePath = path.dirname(result.stdout.toString()); if (result.status === 0 && directoryExistsSync(packagePath)) { return packagePath; } return path.join('node_modules', 'react-native'); } function getHermescDirPathInHermesCompilerPackage() { const result = childProcess.spawnSync('node', [ '--print', "require.resolve('hermes-compiler/package.json')", ]); const packagePath = path.dirname(result.stdout.toString()); const hermescDirPath = path.join(packagePath, 'hermesc'); if (result.status === 0 && directoryExistsSync(hermescDirPath)) { return hermescDirPath; } return null; } function getHermesCompilerPath() { const hermescDirPath = getHermescDirPathInHermesCompilerPackage(); if (hermescDirPath) { // Since react-native 0.83, Hermes compiler executables are in 'hermes-compiler' package return hermescDirPath; } else { return path.join(getReactNativePackagePath(), 'sdks', 'hermesc'); } } function directoryExistsSync(dirname) { try { return fs.statSync(dirname).isDirectory(); } catch (err) { if (err.code !== 'ENOENT') { throw err; } } return false; }