UNPKG

@revopush/react-native-code-push

Version:

React Native plugin for the CodePush service

125 lines (104 loc) 4.65 kB
/* * This script generates a hash of all the React Native bundled assets and writes it into * into the APK. The hash in "updateCheck" requests to prevent downloading an identical * update to the one already present in the binary. * * It first creates a snapshot of the contents in the resource directory by creating * a map with the modified time of all the files in the directory. It then compares this * snapshot with the one saved earlier in "recordFilesBeforeBundleCommand.js" to figure * out which files were generated by the "react-native bundle" command. It then computes * the hash for each file to generate a manifest, and then computes a hash over the entire * manifest to generate the final hash, which is saved to the APK's assets directory. */ var crypto = require("crypto"); var fs = require("fs"); var path = require("path"); var getFilesInFolder = require("./getFilesInFolder"); var CODE_PUSH_FOLDER_PREFIX = "CodePush"; var CODE_PUSH_HASH_FILE_NAME = "CodePushHash"; var CODE_PUSH_HASH_OLD_FILE_NAME = "CodePushHash.json"; var HASH_ALGORITHM = "sha256"; var resourcesDir = process.argv[2]; var jsBundleFilePath = process.argv[3]; var assetsDir = process.argv[4]; var tempFileName = process.argv[5]; var oldFileToModifiedTimeMap = {}; var tempFileLocalPath = null; if (tempFileName) { tempFileLocalPath = path.join(require("os").tmpdir(), tempFileName); oldFileToModifiedTimeMap = require(tempFileLocalPath); } var resourceFiles = []; getFilesInFolder(resourcesDir, resourceFiles); var newFileToModifiedTimeMap = {}; resourceFiles.forEach(function(resourceFile) { newFileToModifiedTimeMap[resourceFile.path.substring(resourcesDir.length)] = resourceFile.mtime; }); var bundleGeneratedAssetFiles = []; for (var newFilePath in newFileToModifiedTimeMap) { if (!oldFileToModifiedTimeMap[newFilePath] || oldFileToModifiedTimeMap[newFilePath] < newFileToModifiedTimeMap[newFilePath].getTime()) { bundleGeneratedAssetFiles.push(newFilePath); } } var manifest = []; if (bundleGeneratedAssetFiles.length) { bundleGeneratedAssetFiles.forEach(function(assetFile) { // Generate hash for each asset file addFileToManifest(resourcesDir, assetFile, manifest, function() { if (manifest.length === bundleGeneratedAssetFiles.length) { addJsBundleAndMetaToManifest(); } }); }); } else { addJsBundleAndMetaToManifest(); } function addJsBundleAndMetaToManifest() { addFileToManifest(path.dirname(jsBundleFilePath), path.basename(jsBundleFilePath), manifest, function() { var jsBundleMetaFilePath = jsBundleFilePath + ".meta"; addFileToManifest(path.dirname(jsBundleMetaFilePath), path.basename(jsBundleMetaFilePath), manifest, function() { manifest = manifest.sort(); var finalHash = crypto.createHash(HASH_ALGORITHM) .update(JSON.stringify(manifest)) .digest("hex"); console.log(finalHash); var savedResourcesManifestPath = assetsDir + "/" + CODE_PUSH_HASH_FILE_NAME; fs.writeFileSync(savedResourcesManifestPath, finalHash); // "CodePushHash.json" file name breaks flow type checking. // To fix the issue we need to delete "CodePushHash.json" file and // use "CodePushHash" file name instead to store the hash value. // Relates to https://github.com/microsoft/react-native-code-push/issues/577 var oldSavedResourcesManifestPath = assetsDir + "/" + CODE_PUSH_HASH_OLD_FILE_NAME; if (fs.existsSync(oldSavedResourcesManifestPath)) { fs.unlinkSync(oldSavedResourcesManifestPath); } }); }); } function addFileToManifest(folder, assetFile, manifest, done) { var fullFilePath = path.join(folder, assetFile); if (!fileExists(fullFilePath)) { done(); return; } var readStream = fs.createReadStream(path.join(folder, assetFile)); var hashStream = crypto.createHash(HASH_ALGORITHM); readStream.pipe(hashStream) .on("error", function(error) { throw error; }) .on("finish", function() { hashStream.end(); var buffer = hashStream.read(); var fileHash = buffer.toString("hex"); manifest.push(path.join(CODE_PUSH_FOLDER_PREFIX, assetFile).replace(/\\/g, "/") + ":" + fileHash); done(); }); } function fileExists(file) { try { return fs.statSync(file).isFile(); } catch (e) { return false; } } if (tempFileLocalPath) { fs.unlinkSync(tempFileLocalPath); }