35

All my experience with exporting/importing modules has come in ES6 using export and import, where you can do something like this to have a single module export a default function as well as separate named functions.

// module.js
export default mainFunction
export { namedFunction }

// main.js
import mainFunction from 'functions'
mainFunction()

import { namedFunction } from 'function'
namedFunction()

However I can't figure out how to do this with ES5 style imports using module.exports and require. As far as I understand, I can export either a single default:

// module.js
module.exports = function mainFunction() {}

// main.js
const mainFunction = require('module.js')

Or I can create named exports:

// module.js
module.exports = {
  namedFunction: function() {}
}

// main.js
const namedFunction = require('module.js').namedFunction

But I can't do both. I thought I could maybe name one of the exports "default" like this, but it doesn't work

// module.js
module.exports = {
  default: function() {},
  namedFunction: function() {}
}

// main.js
const mainFunction = require('module.js') // does not work
const mainFunction = require('module.js').default // works, but not what I want
const namedFunction = require('module.js').namedFunction

How can I accomplish this dual default/named export with ES5?

Ryan Giglio
  • 1,005
  • 1
  • 14
  • 25
  • Exports in ES6 (compiled with babel) export default to a property named `default` and set a property `_esModule`. When importing the default module, it will import `default` iff `_esModule` exists. Otherwise, it imports the whole module as the default. This behavior has caused some [confusion](https://github.com/babel/babel/issues/2212). – Garrett Motzner Jan 04 '19 at 23:27

3 Answers3

62

You want to assign the value of module.exports to be your default function, and then put all the named exports as properties on that function.

const defaultFunction = () => { console.log('default!'); };
const namedFunction1 = () => { console.log('1!'); };
const namedFunction2 = () => { console.log('2!'); };

const myModule = module.exports = defaultFunction;
myModule.namedFunction1 = namedFunction1;
myModule.namedFunction2 = namedFunction2;

Let's say that was in myModule.js. Then you can do this:

const myModule = require('./myModule');
myModule(); // Prints: 'default!'
myModule.namedFunction1(); // Prints: '1!'
Trott
  • 59,820
  • 22
  • 153
  • 197
  • 8
    I'm assuming there is a reason, but why not just assign directly to module.exports? I tried it and it appears to work fine: `module.exports = defaultFunction; module.exports.namedFunction1 = namedFunction1;` – arichards Dec 30 '19 at 08:41
  • 1
    @arichards If that's easier/better for you, then that should work. If I'm reasonably sure the function will be called `myModule` where it is used, then I like to call it `myModule` where it is defined as well. But that's just my preference. – Trott Dec 30 '19 at 15:38
3

Just re-assign exports to module.exports

Like:

//name.js

const NameType = {
  foo: "bar",
};

function Name(name) {
  this.name = name;
}

module.exports = Name; // assign default export to Name
exports = module.exports; // re-assign exports to point it to the updated location.

exports.NameType = NameType; // now you can use named export as usual

In another file you can import like:

const Name = require("./name");
const { NameType } = require("./name");

This works because by default module = { exports: {} } and exports = module.exports

Reference: Difference between "module.exports" and "exports" in the CommonJs Module System

KJ Sudarshan
  • 1,497
  • 1
  • 17
  • 18
0

If you don't want to repeat the named exports, you can always do something like

module.exports = defaultFunction;
module.exports = Object.assign(module.exports, {
    namedFunction1,
    namedFunction2,
});

Then you can require as normal;

const myModule = require('./myModule');
myModule();
myModule.namedFunction1();
Shannon Hochkins
  • 10,888
  • 14
  • 56
  • 90