@babel/runtime is a true dependency

2020-09-13

 | 

~3 min read

 | 

480 words

I’ve been working on nom, a CLI designed to manage a directory of notes. It’s written in Typescript, but I use Babel to handle the code transpilation to vanilla Javascript. Since the use-case for the app is to download it from a registry (e.g., npm) and then use it directly, I am not using a bundler like Webpack.

Most of the time, Babel tooling is a build time process. It transpiles the code and gets out of the way. Because of that, I assumed (wrongly it would turn out) that that’s also true of the @babel/plugin-transform-runtime would also behave that way.

After installing the package, I tried to initialize and was immediately greeted by an error:

% nom init
internal/modules/cjs/loader.js:968
  throw err;
  ^

Error: Cannot find module '@babel/runtime/helpers/interopRequireDefault'
Require stack:
- /Users/stephen/.volta/tools/image/packages/note-mgr-cli/0.0.4-2/dist/index.js
- /Users/stephen/.volta/tools/image/packages/note-mgr-cli/0.0.4-2/index.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:965:15)
    at Function.Module._load (internal/modules/cjs/loader.js:841:27)
    at Module.require (internal/modules/cjs/loader.js:1025:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object.<anonymous> (/Users/stephen/.volta/tools/image/packages/note-mgr-cli/0.0.4-2/dist/index.js:3:30)
    at Module._compile (internal/modules/cjs/loader.js:1137:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)
    at Module.load (internal/modules/cjs/loader.js:985:32)
    at Function.Module._load (internal/modules/cjs/loader.js:878:14)
    at Module.require (internal/modules/cjs/loader.js:1025:19) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/Users/stephen/.volta/tools/image/packages/note-mgr-cli/0.0.4-2/dist/index.js',
    '/Users/stephen/.volta/tools/image/packages/note-mgr-cli/0.0.4-2/index.js'
  ]
}

The stacktrace points to dist/index.js as the source of the issue and looking at it, it was clear what the problem was:

dist/index.js
"use strict"

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault")

The file required @babel/runtime, but that wasn’t in node_modules because I’d listed @babel/plugin-transform-runtime as a devDependencies in my package.json along with all of my other build time dependencies (most of which are Babel related) and didn’t include @babel/runtime as a dependency at all:

note-mgr-cli/package.json
{
  "devDependencies": {
    "@babel/cli": "^7.11.6",
    "@babel/core": "^7.10.5",
    "@babel/plugin-transform-runtime": "^7.10.5",
    "@babel/plugin-proposal-class-properties": "^7.10.4",
    "@babel/preset-env": "^7.10.4",
    "@babel/preset-typescript": "^7.10.4",
    "@types/commander": "^2.12.2",
    "@types/inquirer": "^7.3.1",
    "@types/lodash.kebabcase": "^4.1.6",
    "@types/node": "^14.0.24",
    "husky": "^4.2.5",
    "lint-staged": "^10.2.11",
    "prettier": "^2.0.5",
    "rimraf": "^3.0.2",
    "typescript": "^4.0.2"
  },
  "dependencies": {
    "build": "^0.1.4",
    "chalk": "^4.1.0",
    "commander": "^6.1.0",
    "dayjs": "^1.8.31",
    "dotenv": "^8.2.0",
    "gray-matter": "^4.0.2",
    "inquirer": "^7.3.2",
    "inquirer-fuzzy-path": "^2.3.0",
    "lodash.kebabcase": "^4.1.1"
  }
}

The Babel runtime packages, however, unlike other Babel tools are true dependencies as their entire purpose is to be used at runtime (once I made that connection mentally, this whole issue which cost me several hours to debug felt very clear).

I could have also saved quite a bit of time by reviewing the Babel docs sooner as they make the case very clearly (my emphasis):

Usage

This is meant to be used as a runtime dependency along with the Babel plugin @babel/plugin-transform-runtime. Please check out the documentation in that package for usage.

And even earlier, in the installation directive Babel specifies --save (unlike most other @babel packages which are --save-dev):

npm install --save @babel/runtime

Interestingly, the plugin itself is still a development dependency only. It requires @babel/runtime is listed as a dependency in order to work though. Again, if I had taken the time to read the docs I’d have seen this quickly. Another reason to always check the docs early during a debug session.


Related Posts
  • Getting Typescript, Babel, and ESLint Working Together


  • Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!