Jé pa l'temps #9 - Node.js hacks

🎲 Insert in an array at index

Array.prototype.insert = function (index, item) {
    this.splice(index, 0, item);
};

🪓 Delete undefined and null properties from an object

Object.entries(a).reduce((a,[k,v]) => (v === null || v === undefined ? a : {...a, [k]:v}), {})

» Example

{
  b: null,
  c: undefined,
  d: 1,
  e: '1',
  f: '',
};

// becomes
{
  d: 1,
  e: '1',
  f: '',
};

🛠️ Sort keys of an object

function sortObject(obj) {
  return Object.keys(obj).sort().reduce((result, key) => {
    result[key] = obj[key];
    return result;
  }, {});
}

🎉 Get properties which change between two objects

function getObjectDiff(obj1, obj2) {
  const diff = Object.keys(obj1).reduce((result, key) => {
    if (!obj2.hasOwnProperty(key)) {
      result.push(key);
    } else if (_.isEqual(obj1[key], obj2[key])) {
      const resultKeyIndex = result.indexOf(key);
      result.splice(resultKeyIndex, 1);
    }
    return result;
  }, Object.keys(obj2));

  return diff;
}

💛 SQL request with templating

function sql(query, args) {
  return {
      query: query.join('?'),
       args,
   };
}

sql`SELECT * FROM users WHERE username = ${username}`;

💡 Show all express route

function print (path, layer) {
  if (layer.route) {
    layer.route.stack.forEach(print.bind(null, path.concat(split(layer.route.path))))
  } else if (layer.name === 'router' && layer.handle.stack) {
    layer.handle.stack.forEach(print.bind(null, path.concat(split(layer.regexp))))
  } else if (layer.method) {
    console.log('%s /%s',
      layer.method.toUpperCase(),
      path.concat(split(layer.regexp)).filter(Boolean).join('/'))
  }
}

function split (thing) {
  if (typeof thing === 'string') {
    return thing.split('/')
  } else if (thing.fast_slash) {
    return ''
  } else {
    var match = thing.toString()
      .replace('\\/?', '')
      .replace('(?=\\/|$)', '$')
      .match(/^\/\^((?:\\[.*+?^${}()|[\]\\\/]|[^.*+?^${}()|[\]\\\/])*)\$\//)
    return match
      ? match[1].replace(/\\(.)/g, '$1').split('/')
      : '<complex:' + thing.toString() + '>'
  }
}

app._router.stack.forEach(print.bind(null, []))

⑹ Reduce error stack trace size

Error.stackTraceLimit = 6; // Set the size you want

👉 Use coverage with mocha watch

nodemon --exec nyc --reporter=lcov --reporter=text mocha

📕 Merge two pdfs together

const hummus = require("hummus");
const memoryStreams = require("memory-streams");

/**
 * Concatenate two PDFs in Buffers
 * @param {Buffer} firstBuffer
 * @param {Buffer} secondBuffer
 * @returns {Buffer} - a Buffer containing the concactenated PDFs
 */
export const combinePDFBuffers = (firstBuffer: any, secondBuffer: any) => {
  var outStream = new memoryStreams.WritableStream();

  try {
    var firstPDFStream = new hummus.PDFRStreamForBuffer(firstBuffer);
    var secondPDFStream = new hummus.PDFRStreamForBuffer(secondBuffer);

    var pdfWriter = hummus.createWriterToModify(
      firstPDFStream,
      new hummus.PDFStreamForResponse(outStream)
    );
    pdfWriter.appendPDFPagesFromPDF(secondPDFStream);
    pdfWriter.end();
    var newBuffer = outStream.toBuffer();
    outStream.end();

    return newBuffer;
  } catch (e) {
    outStream.end();
    throw new Error(`Error during PDF combination: ${e.message}`);
  }
};

🪡 Parallel requester

const EventEmitter = require('events');

class ParallelRequester extends EventEmitter {
  constructor({ maxRequests = 4, delay = 0 } = {}) {
    super();

    this.maxRequests = maxRequests;
    this.delay = delay;
    this.queue = [];
    this.results = [];
    this.executingRequests = 0;

    this._endRequest = this._endRequest.bind(this);
    this._executeNext = this._executeNext.bind(this);

    this.on('execute', this._executeNext);
  }

  add(req) {
    if (Array.isArray(req)) {
      this.queue.push(...req);
    } else {
      this.queue.push(req);
    }

    return this;
  }

  start() {
    if (this.executingRequests < this.maxRequests) {
      this.emit('execute');
    }

    return this;
  }

  end() {
    if (!this.executingRequests && !this.queue.length) {
      return [];
    }

    return new Promise((resolve) => {
      this.on('end', () => {
        resolve(this.results);
      });
    });
  }

  _endRequest() {
    this.executingRequests--;

    if (this.executingRequests < this.maxRequests && this.queue.length) {
      setTimeout(() => {
        this.emit('execute');
      }, this.delay);
    }

    if (!this.executingRequests && !this.queue.length) {
      this.emit('end');
    }
  }

  _executeNext() {
    // console.log('Executing request - ', this.executingRequests, ' currently');
    while (this.executingRequests < this.maxRequests && this.queue.length) {
      this.executingRequests++;

      const request = this.queue.shift();

      Promise.resolve(request())
        .then((data) => {
          this.results.push(data);
          this._endRequest();
        })
        .catch(() => {
          this.results.push([]);
          this._endRequest();
        });
    }
  }
}

module.exports = {
  ParallelRequester,
};

— — — — — — — — — — — — — — — — — — — — — —

La série « Jé pa l’temps » est une série de tutoriels rapides en mode “prise de note” pour avoir une trace de tout ce dont je ne peux me rappeler et pourquoi pas le partager à d’autre. On va à l’essentiel, laissons les jolis pavés à d’autres sites comme medium… LOL !