babel-plugin-dynamic-import-node

A Babel plugin that rewrites ES `import()` calls into a Promise-wrapped `require()` for Node.js environments.

Tool
npm
v2.3.3
574stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
36/100Needs Attention
Development Activity0
Maintenance0
Community64
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
46/100Fair
Architecture60
Code Quality70
Innovation30
Learning Curve25

babel-plugin-dynamic-import-node solves a narrow but common compatibility gap: code written with the ES dynamic import() proposal needs to run somewhere that only understands CommonJS require(), such as an older Node runtime, a Jest test file, or any tool in a build pipeline that hasn’t adopted native ESM. The plugin hooks into Babel’s parser to recognize import() syntax, then rewrites each call site into Promise.resolve().then(() => require(source)) (or a template-literal variant when the import source isn’t a static string), optionally interop-wrapping the result so default exports from CommonJS modules behave as expected.

It ships as a single-purpose visitor plugin with no configuration surface beyond a noInterop flag, and it deliberately supports both Babel 6 and Babel 7 side by side so consuming projects can adopt it during a Babel major-version migration. Its main real-world footprint is as the transform used internally by babel-jest and Create React App’s Babel preset to make import() usable inside test files that don’t run through a bundler.

What You Get

  • A single Babel visitor that intercepts Import AST nodes and swaps them for a require()-based equivalent, with no other configuration needed beyond adding the plugin to .babelrc
  • Automatic interop handling so import() of a CommonJS module still resolves to the expected default-export shape, unless the noInterop option is set
  • Support for both static string import sources and fully dynamic (template-literal or expression) import sources, each compiled to a matching code path
  • Dual compatibility with Babel 6 (babel-core) and Babel 7 (@babel/core), so the same plugin works unmodified across a Babel major-version upgrade
  • A de-duplication guard (via WeakSet) that prevents the same import() call from being transformed twice when multiple Babel passes visit the same AST

Common Use Cases

  • Running Jest test suites that contain import() calls, since Jest transforms files through Babel rather than a bundler and has no native support for dynamic import on its own
  • Supporting dynamic import() syntax in Node.js versions or build pipelines that predate native ESM/dynamic-import support
  • Migrating a codebase from Babel 6 to Babel 7 while keeping dynamic-import transformation working identically on both versions during the transition
  • Providing Node-compatible output for isomorphic code that also targets a bundler (webpack) via a sibling plugin, so the same source can be built for both client and server targets

Under The Hood

Architecture The plugin is deliberately minimal: src/index.js registers a single Babel visitor for Import AST nodes and delegates the actual rewrite to createDynamicImportTransform in src/utils.js. That factory builds four code templates upfront (static-source/dynamic-source, each with an interop and no-interop variant) using Babel’s template helper, then returns a visitor callback that picks the right template per call site and swaps the compiled expression in via path.parentPath.replaceWith. A WeakSet of already-visited paths guards against double-transformation across multiple compiler passes. With only two small source files and no internal layering beyond “plugin registration” versus “transform factory,” there is little architectural surface to change — a modification to the core Promise/require templates is effectively the only place the transform’s output shape can shift.

Tech Stack Written in plain ES module JavaScript, compiled to CommonJS via babel-cli (babel src --out-dir lib) for publishing. It targets two Babel major versions simultaneously — babel-core for Babel 6 and @babel/core for Babel 7 — both listed as dev dependencies alongside their respective preset/plugin ecosystems. The only runtime dependency is object.assign, a spec-compliant Object.assign polyfill for older Node targets. Testing runs on tape with airbnb-js-shims and babel-register providing on-the-fly transpilation, and CI runs across a wide Node version matrix (0.12 through 13) via Travis.

Code Quality The test suite (test/index.js + test/testPlugin.js) drives a fixture-based comparison: for each scenario under test/fixtures/* (basic import, dynamic argument, template argument, chained/nested imports, imports with comments, non-string arguments), it runs the plugin through both Babel 6 and Babel 7 transform pipelines and diffs the output against checked-in expected.*.js files for each interop/es2015 permutation — a thorough matrix given the codebase’s small size. ESLint (Airbnb config) is enforced as a pretest step that fails the test run on lint errors. There is no TypeScript or runtime type checking; the code is plain, untyped JavaScript, and error handling is minimal since the transform has almost no failure modes to guard against.

API Design The plugin’s entire public surface is a single Babel .babelrc entry ("dynamic-import-node") plus one optional boolean option, noInterop. There is no exported JS API beyond the default Babel-plugin function itself — consumers never import or call anything from this package directly in application code; it only participates through Babel’s plugin-loading mechanism. That minimalism keeps onboarding trivial (one line of Babel config) at the cost of zero flexibility beyond the interop toggle.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search