0

I am looking for a way to keep functions declared in module scope, export them, and allow them to be wrapped in a completely different file, and export a wrapped copy of all modules. The issue: The wrap works, but it only wraps the called function, that function is still calling the unwrapped counter-parts. Without changing the scope of the original authored modules, is there any way to tell the log version, to use only other logged version?

colors.js

let green = () => 'green'
let red = () => 'red'
let purple = () => 'purple'
let colors = () => green() + red() + purple()

module.exports = {
  red,
  green,
  purple,
  colors
}

colors.log.js

const {logWrapFns} = require('./index')
let {
  red,
  green,
  purple,
  colors
} = require('./colors')

;[red, green, purple, colors] = logWrapFns('color')([
  red,
  green,
  purple,
  colors
])

module.exports = {
  red,
  green,
  purple,
  colors
}

debugFns.js

const {map, mapValues, get} = require('lodash')
const debug = require('debug')

const logWrapFn = (scope) => (fn, key) => (...input) => {
  const d = debug(`${scope}:${fn.name || key}`)
  d(JSON.stringify({input}))
  const possiblePromise = fn.apply(null, input)
  if (get(possiblePromise, 'then')) {
    return possiblePromise.then(output => {
      d(JSON.stringify({output}))
      return output
    })
  } else {
    d(JSON.stringify({output: possiblePromise}))
    return possiblePromise
  }
}
const logWrapFnsReturnObj = (scope) => (fns) => mapValues(fns, logWrapFn(scope))
const logWrapFnsReturnArr = (scope) => (fns) => map(fns, logWrapFn(scope))

module.exports = {
  logWrapFn,
  logWrapFns
}

usage.js

const colors = require('./colors.log')

console.log(colors)

console.log(colors.green())

Only logging debug statement for green not the rest.

I know this is possible if I author the module with a class and use static methods, however then I have to call each method with the extra class name, which is something I am trying to avoid.

ThomasReggi
  • 55,053
  • 85
  • 237
  • 424

1 Answers1

0

No, it is not possible to make the original colors function to call something else than the original green/red/purple functions without modifying the original module code.

You don't need to use a class with static methods (you shouldn't do that anyway). I recommend to rewrite your module to

// notice this === exports
this.green = () => 'green'
this.red = () => 'red'
this.purple = () => 'purple'
this.colors = () => this.green() + this.red() + this.purple()

in which colors will call the other functions as methods of the module.exports object, where you can replace them by the wrapped versions:

// colors.log.js
const logWrap = require('./debugFns').logWrapFn('color')
const c = module.exports = require('./colors')

c.red = logWrap(c.red)
c.green = logWrap(c.green)
c.purple = logWrap(c.purple)
c.colors = logWrap(c.colors)
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Is there a way to alter the original authored modules to support it? – ThomasReggi Feb 24 '18 at 04:14
  • Interesting use of `this`, really impressive stuff. – ThomasReggi Feb 24 '18 at 04:35
  • Added an enhancment to this via using `mapValues` in the `logWrapFns` and exporting this from `colors.log.js` `module.exports = Object.assign(c, logWrap(c))` – ThomasReggi Feb 24 '18 at 04:35
  • @ThomasReggi Actually the use of `this` is quite pointless, one should in general prefer `exports` for clarity and for being able to use it in `function`s/methods. It's only three characters longer :-) – Bergi Feb 24 '18 at 04:41
  • @ThomasReggi Yeah, a loop might be sensible if you want to wrap everything by default. – Bergi Feb 24 '18 at 04:43
  • Babel interpolation breaks a file with `this` https://babeljs.io/repl/#?babili=false&browsers=&build=&builtIns=false&code_lz=C4CwlgzgdA5gTgUwQOwAQF5UAoCUGB8qA5PEskQFCiRSIAmG2e6hR9l10ADgK5xcAbBI1wFivfkI7hoAYwD2A-XAgjmhTrEQpRAalSb6egzKgTBCXBSA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&lineWrap=true&presets=es2015%2Ces2016%2Creact%2Cstage-1%2Cstage-2%2Cstage-3&prettier=false&targets=&version=6.26.0&envVersion= – ThomasReggi Feb 24 '18 at 04:43
  • @ThomasReggi Looks like babel repl interprets it as an ES6 module, which it is not – Bergi Feb 24 '18 at 05:02