Node modules can install their own code, which is located in the same directory as the source code of the cloud function. I found that some node libraries on github that do this for ghostscript, which is a very useful library for processing PDFs:
I put lambda-ghostscript in a subdirectory of my functions directory and then add node-gs as a dependency in my package file as follows:
{ "name": "functions", "dependencies": { "@google-cloud/storage": "^1.3.1", "child-process-promise": "^2.2.1", "firebase-admin": "~5.4.0", "firebase-functions": "^0.7.2", "gs": "https://github.com/sina-masnadi/node-gs/tarball/master" } }
Then in my index.js file, I can simply require the node library to easily use ghostscript from JavaScript. Here's the full code for the cloud feature that uses the Google Cloud Storage trigger:
const functions = require('firebase-functions'); const gcs = require('@google-cloud/storage')(); const spawn = require('child-process-promise').spawn; const path = require('path'); const os = require('os'); const fs = require('fs'); var gs = require('gs'); exports.makePNG = functions.storage.object().onChange(event => { // ignore delete events if (event.data.resourceState == 'not_exists') return false; const filePath = event.data.name; const fileDir = path.dirname(filePath); const fileName = path.basename(filePath); const tempFilePath = path.join(os.tmpdir(), fileName); if (fileName.endsWith('.png')) return false; if (!fileName.endsWith('.pdf')) return false; const newName = path.basename(filePath, '.pdf') + '.png'; const tempNewPath = path.join(os.tmpdir(), newName); // // Download file from bucket. const bucket = gcs.bucket(event.data.bucket); return bucket.file(filePath).download({ destination: tempFilePath }).then(() => { console.log('Image downloaded locally to', tempFilePath); return new Promise(function (resolve, reject) { gs() .batch() .nopause() .option('-r' + 50 * 2) .option('-dDownScaleFactor=2') .executablePath('lambda-ghostscript/bin/./gs') .device('png16m') .output(tempNewPath) .input(tempFilePath) .exec(function (err, stdout, stderr) { if (!err) { console.log('gs executed w/o error'); console.log('stdout',stdout); console.log('stderr',stderr); resolve(); } else { console.log('gs error:', err); reject(err); } }); }); }).then(() => { console.log('PNG created at', tempNewPath); // Uploading the thumbnail. return bucket.upload(tempNewPath, {destination: newName}); // Once the thumbnail has been uploaded delete the local file to free up disk space. }).then(() => { fs.unlinkSync(tempNewPath); fs.unlinkSync(tempFilePath); }).catch((err) => { console.log('exception:', err); return err; }); });
Here's a github project: https://github.com/ultrasaurus/ghostscript-cloud-function
Disclaimer: This is using compiled source code, and I tested experimentally what works for this case, so this is probably good. I did not consider specific compilation options and did not check whether they were correctly fixed for the environment of cloud functions.