Nodejs: Good practice to just use the index.js to EXPORTS?
Asked Answered
M

5

26

I am seeing a pattern on some code I have inherited. Each directory has its JS file but there is also a index.js that actually exports items from the other JS file or files.

I presume this is done so you can see exactly what you are exporting, as the main exports are in index.js and the main code is in the other js file or files.

Is this correct? What is this pattern called ?

Should I continue using this pattern.

Marnimarnia answered 24/5, 2016 at 15:7 Comment(2)
not needed, index.js is required to link the package index field to the index file. but within a module you can load any file with its relative path.Equatorial
it's just shorthand to require whole directory.Crumble
S
26

Let's say I have the following directory structure:

MyApp
├── app.js
├── test.js
├── package.json
├─┬ controllers
│ ├── index.js
│ ├── signIn.js
│ └── signOut.js
└─┬ views
  ├── index.js
  ├── signIn.js
  └── signOut.js

Placing the following code inside the index.js files...

// index.js
module.exports = {
  signIn: require('./signIn')
, signOut: require('./signOut')
};

...allows you to require an entire directory like...

// test.js
describe('controllers', () => {
  // ~/controllers/index.js
  const controllers = require('./controllers');

  it('performs a sign-in', () => {
    ...
  });
  it('performs a sign-out', () => {
    ...
  });
});

The alternative is to require each file individually.

Having an index.js in a directory is not required. You may require a file in a directory without an index.js all the same.

// app.js
const signOut = require('./controllers/signOut.js')

However, it gets tedious as your app grows. I use a package like require-directory as typing out each file in a directory is also tedious and somewhat error prone.

// index.js
module.exports = require('require-directory')(module);

/*

This yields the same result as:

module.exports = {
  signIn: require('./signIn')
, signOut: require('./signOut')
, ...
};

*/
Springhalt answered 24/5, 2016 at 17:1 Comment(0)
C
17

ES6 CommonJS Module syntax

Given these two common types of structures...

MyApp
│ // files divided per type (controllers, components, actions, ...)
├─┬ actions
│ ├── index.js
│ ├── signIn.js
│ └── signOut.js
├─┬ components ...
├─┬ reducers ...
├─┬ pages ...
│ 
│ // files divided per component
├─┬ components ...
│ ├── index.js 
│ ├── SimpleComponent.jsx
│ ├── AnotherComponent.duck.jsx // redux "duck" pattern
│ ├─┬ ComplexComponent // large complex logic, own actions, stylesheet, etc.
│ ...
├─┬ pages ...
│ ├── index.js 
│ ├─┬ App
│ │ ├── index.js // not necessary here, matter of habit
│ │ ├── App.jsx
│ │ ├── actions.js
│ │ └── reducer.js
│ └─┬ Dashboard
├── another.js
...

You can simply import files in another.js like this

import {signIn, signOut} from './actions'
import {App} from './pages'
import {ComplexComponent} from './components'

instead of this (without index.js files)

import {signIn} from './actions/signIn'
import {signOut} from './actions/signOut'
import {App} from './pages/App/App' //notice the redundancy here
import {ComplexComponent} from './components/ComplexComponent/ComplexComponent'

More reading

ECMAScript 6 modules
import - JavaScript | MDN
Babel transpiler - brings the new imports to your browser now

Structuring React projects
React Redux "Ducks pattern" - a single file approach for components

Commissariat answered 27/10, 2017 at 13:21 Comment(0)
V
12

The other answers provide a lot of great information, but to try and specifically answer your question 'Should I continue using this pattern", I'd say no, at least most of the time.

The thing is, this pattern requires extra effort, as you have to maintain those extra index.js files. In my experience that effort is greater than the effort to simply write one-directory-longer import statements. Plus, you can get the same functionality you'd get from having an index.js without one, by using a module like require-dir.

All that being said, if you are making a library that will be consumed by a large number of people, like a critical module in a large programming department, or a public NPM module, then the effort of an index.js becomes more justified. As long as you have enough people using your modules, your users will (cumulatively) save more time from you adding them than you will lose maintaining them.

Vereen answered 21/1, 2018 at 2:21 Comment(0)
E
4

I will directly dive into your question on whether to use this pattern or not (as other answers are not sufficient for this).

Assuming that each directory in your code represents a standalone module (doesn't rely on another module to work). Using this pattern will give these advantages:

  1. Better and more organized imports
  2. Separation between internal/external definitions of each module (similar to using private/public on an interface/API)

The problems with this:

  1. It can be very tiresome to keep loose-coupling of the different modules (JS/TS is not pure OOP)
  2. Requires active refactoring to modules definition - more circular dependencies.
  3. Loads more code to memory (even if unused) - though I'm not sure how bad this can be as there are optimizations that usually fix this problem when bundling production code.

Circular dependencies are very problematic, importing the whole module/directory using index.js will import all of its parts (that are declared in index.js) so if you have:

-- moduleA 
├-- comp1A // needs comp1B
├-- comp2A     
└-- index.js // export both comp1/2

-- moduleB
├-- comp1B 
├-- comp2B // needs comp2A
└-- index.js // export both comp1/2

Example case - comp1A needs something from comp1B while comp2B needs something from comp2A

When importing the specific files (without index.js - import something from './moduleB/comp1B') you won't have circular dependencies.

But if you use index.js (import something from './moduleB') you will have circular dependencies.

My recommendation is to use index.js in the right places, and to keep those maintained! Using index.js with small modules will be perfect, but with time they will grow and should be divided. index.js is very bad to use in common/shared/utils/misc/core (whatever you call it when you want to put uncategorized and unrelated code that is used across your whole project) module.

Endaendall answered 21/7, 2021 at 13:50 Comment(1)
Amen to circular dependencies. index files make sense as an external interface but I've been spending the past number of days disentangling hundreds of circular dependencies in some code I inherited.Siloa
P
1

What about this?

module.exports = {
  ...require('./moduleA'),
  ...require('./moduleB')
}

(moduleA.a will be overridden by moduleB.a)

Primavera answered 16/5, 2021 at 8:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.