JavaScript Best Practices — Imports
By: John Au-Yeung
Tags: Technology, Software Development, JavaScript, Programming, Web Development

Photo by Olav Tvedt on Unsplash

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at some best practices for importing items.

No Imports that can’t be Resolved

We shouldn’t import modules that can’t be resolved.

This means that we make sure that the module is found before we import them.

For ES6 modules, if we write:

import x from './foo'

then we should make sure foo has a default export.

If we write:

const { default: x } = require('./foo')

Then we should make sure that we have a foo module with a module.exports or exports inside.

Named Imports

We should make sure that named imports are included in the set of named exports in a module.

For instance, we should have:

export const foo = "bar";

in foo.js .

Then we can write:

import { foo } from './foo'

or:

export { foo as bar } from './foo'

to import it.

Default Imports

If we want to make a default import, then we should also make sure it exists.

If we have:

export default function () { return 42 }

in foo.js , or:

export const bar = () => { return null }

in bar.js , or:

module.exports = function () { /* ... */ }

in baz.js .

Then we can import them:

import foo from './foo'

or:

import { bar } from './bar'

or:

const baz = require('./baz');

Import a Namespace

We can namespace our exports if they aren’t default exports.

For instance, we can write:

export const a = 1

or:

const b = 2

export { b }

or:

const c = 3

export { c as d }

Given that they’re all in foo.js , we can import them by writing:

import { a } from './foo';

or:

import { b } from './foo';

or:

import { d } from './foo';

We can also combine more than one of them into one.

Absolute paths

We shouldn’t use absolute paths when we’re importing modules.

Therefore, the path must always have a period if we’re importing project files.

For instance, we shouldn't write:

import f from '/foo';

or:

const f = require('/foo');

Rather, we should write:

import foo from './foo';

or:

const foo = require('./foo');

If we’re importing a module, then we don’t need the slash and the dot.

For instance, we can write:

import _ from 'lodash';

or:

const _ = require('lodash');

Absolute paths are bad since they don’t work all in all locations.

If we move our project to a different part, then the imports will be broken.

No require Calls with Expressions

Using require with expressions is a bad idea. We don’t want to give attackers a chance to change the import path to import malicious code.

require calls are resolved at runtime rather than build time.

Dynamic expressions in require also makes static analysis harder or to find where a module is used in our codebase.

Therefore, instead of writing:

require(name);

require('../' + name);

require(`../${name}`);

require(name());

We write:

require('../foo');

No Internal Modules

We shouldn’t import internal modules that aren’t meant to be imported by external code.

For instance, we shouldn't write:

import { settings } from './app/index';

Instead, we write:

import { settings } from './app';

No Webpack Loader Syntax

We shouldn’t use Webpack’s proprietary syntax so if we migrate off Webpackm then we won’t have so many issues.

Instead of writing:

import theme from 'style!css!./theme.css';

We write:

import theme from './theme.css';

Using standard syntax will save us a lot of trouble in the future.

Don’t Import Itself in a Module

We shouldn’t import itself in a module.

So if we have foo.js , we shouldn't write:

import foo from './foo';

or:

const foo = require('./foo');

Instead, we should write the following in foo.js :

import bar from './bar';

We can also write:

const bar = require('./bar');

No Cyclic Imports

We shouldn’t have cyclic imports in our modules.

A cyclic import would be the following:

a.js :

import './b.js';

b.js :

import './a.js';

We have 2 modules that import dependencies from each other.

Instead, we should write:

a.js :

import './c.js';

to break the cycle.

Photo by Vidar Nordli-Mathisen on Unsplash

Conclusion

We shouldn’t have cyclic imports in our code.

Also, we shouldn't have dynamic imports or imports that can’t be imported.

And we should never have absolute imports.