Javascript module: package and reuse code

This tutorial is a part of the Learn everything about Javascript in one course.

Why do we package and reuse code?

Anything you build, will depend on someone else's code. Why? Because it's fast, saves time, money. When joining a team, working on a project, you will build upon your team's code. Your teammate will also use your code for their work. That's why we need to know how to package and reuse code.

Reusable code is written in a file called module with additional (simple) export syntax. When we reuse this code, we import the module with its name and file path. There are currently 2 ways that you can package and reuse code:

CommonJS ES Modules
Availability Before ES6 Since ES6 (2015)
Node.js support All versions Node 13 and up (to use this feature, you need to add "type": "module" to your package.json file)
Export syntax module.exports = exportObject export exportObject1
export default exportObject2
Import syntax const importedObj = require('/path/to/file.js) import importedObj from '/path/to/file'

CommonJS and ES Modules import/export should not be mixed.

Package (export) your code with CommonJS syntax

You can write your code as normal then export them by then end of the file using syntax: module.exports = exportObject

commonjs/export.js
// Package (export) your code with CommonJS syntax

const getUser = async () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ name: 'Superman', superpower: 'fly'})
    }, 1000);
  })
}

module.exports = { // export syntax
  API_KEY: '123-456',
  getUser
}

Reuse (import) your code with CommonJS syntax

You can import your code (on the top of the file) with CommonJS syntax: const importedObj = require('/path/to/file.js)

commonjs/import.js
// Reuse (import) your code with CommonJS syntax

const { API_KEY, getUser } = require('./export') // destructure while importing a module
const wholeModule = require('./export.js') // import whole module, ".js" part is optional

;(async () => { // a self-invoking async function
  const user1 = await getUser() 
  const user2 = await wholeModule.getUser()
  console.log('user1:', user1)
  console.log('user2:', user2)
})()

console.log('API_KEY:', API_KEY)
console.log('API_KEY:', wholeModule.API_KEY)

// API_KEY: 123-456
// API_KEY: 123-456
// user1: { name: 'Superman', superpower: 'fly' }
// user2: { name: 'Superman', superpower: 'fly' }

Package (export) your code with ES Module syntax

You can write your code as normal then export them by then end of the file using syntax:

  • export default exportObject2, the default object recieved when import.
  • export exportObject1, import needs to destructure to get this exportObject1.
es_module/export.js
// Package (export) your code with ES Module syntax

const getUser = async () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ name: 'Superman', superpower: 'fly'})
    }, 1000);
  })
}

export const API_KEY = '123-456' // import needs to destructure to get this constant

export default getUser // default object recieved when import

For Node version 13 & 14, "type": "module" in package.json file is required to be able to use ES Module export/import syntax.

es_module/package.json
{
  "type": "module"
}

Reuse (import) your code with ES Module syntax

You can import your code (on the top of the file) with CommonJS syntax:

  • import importedObj from '/path/to/file' for default exported object.
  • import { objectName } from '/path/to/file' for non-default exported object.
  • import * as wholeObject from '/path/to/file' for everything and renamed as wholeObject.
  • We can use as to rename imported object.
es_module/import.js
// Reuse (import) your code with ES Module syntax

 // ".js" is required for Node ES Module
import { API_KEY } from './export.js' // destructured import for non-default exported object
import { API_KEY as key } from './export.js' // destructured import and rename
import getUser from './export.js' // import default exported object
import * as wholeModule from './export.js' // import everything and rename as "wholeModule"

;(async () => {
  const user1 = await getUser()
  const user2 = await wholeModule.default()
  console.log('user1:', user1)
  console.log('user2:', user2)
  console.log('wholeModule:', wholeModule)
})()

console.log('API_KEY:', API_KEY)
console.log('key:', key)
// API_KEY: 123-456
// key: 123-456
// user1: { name: 'Superman', superpower: 'fly' }
// user2: { name: 'Superman', superpower: 'fly' }
// wholeModule: [Module] { API_KEY: '123-456', default: [AsyncFunction: getUser] }

Summary

  1. Code packaging and reusing is a very important concept (but it's very simple to learn). You will see this in almost every project you work on.
  2. There are 2 ways to do code packaging and reusing: CommonJS and ES Module.
  3. CommonJS and ES Modules import/export should not be mixed.