42

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?

1
  • 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. Commented Jan 4, 2019 at 23:27

3 Answers 3

79

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!' 
Sign up to request clarification or add additional context in comments.

2 Comments

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 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.
8

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

4 Comments

Hmm.... I am confused... how is module.exports = Name; exports = module.exports; exports.NameType = NameType different than just module.exports = Name; Name.NameType = NameType; (i.e. the accepted answer)? My understanding is that assigning a value to exports doesn't affect the import (that's explained in your ref link), but since you're assigning it to Name you do end up adding .NameType = NameType to Name, even if the change to exports doesn't make it out of the module. Am I missing something?
I guess I don't understand what "the updated location" is in your comments... what is the "updated location"? Isn't exports already assigned to module.exports by default?
@JasonC It's important to re-assign since module.exports = Name; makes it now point to a different object ( Name in this case ). But exports still points to what module.exports was pointing to before. Hence the re-assignment.
Also, the re-assignment is only done to keep the API for named and default exports similar to how one normally uses them. Otherwise. module.exports.NameType also works for the use case intended here. But we prefer using exports.NameType over module.exports.NameType.
1

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(); 

2 Comments

This breaks the nice suggestions that VSCode provides when requireing modules
You mean Intellisense? Interesting

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.