'use strict'; /** * file-locking.js - file system locking (replaces fs-ext) */ let async = require('async'), locker = require('lockfile'), fs = require('fs'), path = require('path'); // locks a file by creating a lock file function lockFile(name, next) { let lockFileName = name + '.lock', lockOpts = { wait: 1000, // time (ms) to wait when checking for stale locks pollPeriod: 100, // how often (ms) to re-check stale locks stale: 5 * 60 * 1000, // locks are considered stale after 5 minutes retries: 100, // number of times to attempt to create a lock retryWait: 100, // time (ms) between tries }; async.series({ statdir: function(callback) { // test to see if the directory exists fs.stat(path.dirname(name), function(err, stats) { if (err) { callback(err); } else if (!stats.isDirectory()) { callback(new Error(path.dirname(name) + ' is not a directory')); } else { callback(null); } }); }, statfile: function(callback) { // test to see if the file to lock exists fs.stat(name, function(err, stats) { if (err) { callback(err); } else if (!stats.isFile()) { callback(new Error(path.dirname(name) + ' is not a file')); } else { callback(null); } }); }, lockfile: function(callback) { // try to lock the file locker.lock(lockFileName, lockOpts, callback); }, }, function(err) { if (err) { // lock failed return next(err); } // lock succeeded return next(null); }); } // unlocks file by removing existing lock file function unlockFile(name, next) { let lockFileName = name + '.lock'; locker.unlock(lockFileName, function(err) { if (err) { return next(err); } return next(null); }); } /** * reads a local file, which involves * optionally taking a lock * reading the file contents * optionally parsing JSON contents */ function readFile(name, options, next) { if (typeof options === 'function' && next === null) { next = options; options = {}; } options = options || {}; options.lock = options.lock || false; options.parse = options.parse || false; function lock(callback) { if (!options.lock) { return callback(null); } lockFile(name, function(err) { if (err) { return callback(err); } return callback(null); }); } function read(callback) { fs.readFile(name, 'utf8', function(err, contents) { if (err) { return callback(err); } callback(null, contents); }); } function parseJSON(contents, callback) { if (!options.parse) { return callback(null, contents); } try { contents = JSON.parse(contents); return callback(null, contents); } catch (err) { return callback(err); } } async.waterfall([ lock, read, parseJSON, ], function(err, result) { if (err) { return next(err); } else { return next(null, result); } }); } exports.lockFile = lockFile; exports.unlockFile = unlockFile; exports.readFile = readFile;