0

I am using NodeJS and have been writing JavaScript for a few years now and am still learning.

For my CJS modules, I write (what I call) a root function that contains all of (what I call) my sub-functions and then return {subfunction1, subfunction2} on the root function for the functions I wanted to expose. Admittedly, I learned this writing style from Jonathan Mills and have been happy with it.

I am struggling with how to migrate this properly from CommonJS to ESM and am hoping to do so without using a Class. However, if Class is the right way with ESM, then I will adapt.

Here is a CJS Service:

service.js

function WebexService(webex) {
  async function processMessage(messageData) {
    try {
      const user = await webex.people.get(messageData.personId);
      //debug(user);
      sendMessage({ displayName: user.displayName, roomId: messageData.roomId });
    } catch (error) {
      debug(error);
      throw error;
    }
  }
  function sendMessage(messageInfo) {
    webex.messages.create({
      roomId: messageInfo.roomId,
      text: `Howdy! ${messageInfo.displayName}`,
    });
  }
  return { processMessage }
}

module.exports = WebexService()

To use the this CJS service, I would import it as:

app.js

const { processMessage } = require('../services/webexService');

function superCool() {
  const messageResponse = await processMessage(messageData);
}

The only way I have been able to get this to work with ESM is as a Class:

service.js

import debugInit from 'debug';
import chalk from 'chalk';
const debug = debugInit('app:services:webex');

export default class WebexService {
  constructor(webex) {
    this.webex = webex;
  }
  async processMessage(messageData) {
    try {
      const user = await this.webex.people.get(messageData.personId);
      //debug(user);
      this.sendMessage({ displayName: user.displayName, roomId: messageData.roomId });
    } catch (error) {
      debug(error);
      throw error;
    }
  }
  sendMessage(messageInfo) {
    this.webex.messages.create({
      roomId: messageInfo.roomId,
      text: `Howdy! ${messageInfo.displayName}`,
    });
  }
}

app.js

import WebexService from '../services/webex.js';

const WebexServiceInstance = new WebexService(webex);
WebexServiceInstance.processMessage(event.data);

I am hopeful someone can point me in the right direction. I'm happy to RTFM if someone can help me find one to read.

Jeremy M
  • 179
  • 2
  • 13
  • 1
    Why does your `WebexService` expect a parameter, but you don't pass anything to it? – Konrad Jul 04 '22 at 12:32
  • Konrad, Good question as I assume you're referring to the CJS example. For this question, since that is not the focus of my question, it is assumed I am properly initiating it and sending `webex` to the `WebexService`. – Jeremy M Jul 04 '22 at 12:35
  • I hope you have mentioned `"type":"module"` in your package.json – Rahul K Jul 04 '22 at 12:39
  • "*I write a root function that contains all of my sub-functions and then `return {subfunction1, subfunction2}` on the root function for the functions I wanted to expose*" - are you calling the root function multiple times or at least in multiple places? If not, this approach seems pretty pointless. – Bergi Jul 04 '22 at 12:50

1 Answers1

0

service.js

function WebexService(webex) {
  async function processMessage(messageData) {
    try {
      const user = await webex.people.get(messageData.personId);
      //debug(user);
      sendMessage({ displayName: user.displayName, roomId: messageData.roomId });
    } catch (error) {
      debug(error);
      throw error;
    }
  }
  function sendMessage(messageInfo) {
    webex.messages.create({
      roomId: messageInfo.roomId,
      text: `Howdy! ${messageInfo.displayName}`,
    });
  }
  return { processMessage }
}

const service = WebexService()
export default service

app.js

import WebexService from '../services/webex.js';
// you can then do this
const { processMessage } = WebexService;
function superCool() {
  const messageResponse = await processMessage(messageData);
}
// or just use it directly
function superCool() {
  const messageResponse = await WebexService.processMessage(messageData);
}
Konrad
  • 21,590
  • 4
  • 28
  • 64
  • Konrad, The above works great. I guess I am a liar though as I thought I had it figured out on how to pass a constructor function `webex` to `WebexService` in ESM. Any ideas on how to pass it? – Jeremy M Jul 04 '22 at 15:59
  • I have no idea how you do it in CommonJS, because you call the function without the argument – Konrad Jul 04 '22 at 16:04
  • In CommJS I would import it into `app.js` as: `const { processMessage } = require('../services/webexService')(webex);` – Jeremy M Jul 04 '22 at 16:10
  • 1
    `export default WebexService` then `import WebexService from '../services/webex.js';` and `const { processMessage } = WebexService(webex)` – Konrad Jul 04 '22 at 16:26
  • 1
    Thank you Konrad. This is why I like the community at Stack Overflow. I've spent over a week trying to figure this out myself. I appreciate you. – Jeremy M Jul 04 '22 at 18:30