npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@actualwave/babel-ioc-dep-wrap-plugin

v0.0.4

Published

Babel plugin that wraps module into container to provide dependencies asynchronously from custom sources.

Readme

Babel IoC Dependency Wrapper Plugin

Wraps a CommonJS module's body in a container function so that require() calls can be intercepted and resolved asynchronously from a custom source (e.g. over HTTP, from a database, or from a sandboxed registry). Two wrapper variants are provided: one based on async/await and one based on generators.

Both wrappers handle ES6 import declarations, dynamic import() calls, and require() calls inside nested functions out of the box — all conversions are enabled by default.

Installation

npm install --save-dev @actualwave/babel-ioc-dep-wrap-plugin

@babel/core is required as a peer dependency:

npm install --save-dev @babel/core

Usage

Both plugins are plain Babel plugin factories — call them to get a plugin and pass the result in the plugins array.

import babel from '@babel/core';
import { wrapWithAsyncFn, wrapWithGeneratorFn } from '@actualwave/babel-ioc-dep-wrap-plugin';

const result = babel.transformSync(sourceCode, {
  plugins: [wrapWithAsyncFn()],
});

Async Wrapper

wrapWithAsyncFn(globalRequire?, options?) wraps the module in an async function. Every require() call is converted to await require() (or await <requireName>()), and dynamic import() at the top level is converted the same way. A custom resolver function is passed as the first argument at call time, letting you intercept every dependency load.

Options

| Argument | Type | Default | Description | |---|---|---|---| | globalRequire | boolean | false | When true, the resolver parameter defaults to the global require, allowing fallback to normal Node.js resolution. | | options.requireName | string | 'require' | Name of the injected resolver parameter and all generated calls. Change this when require conflicts with something in the surrounding scope. | | options.convertImports | boolean | true | Converts ES6 import declarations to await <requireName>() calls automatically. Set to false to throw on any import declaration instead. | | options.hoistNestedRequires | boolean | true | Hoists require() calls found inside nested functions to the top of the wrapper. Set to false to throw on nested require() instead. |

Example — basic

// Input
"use strict";
require('init');
const b = require('b.js');
const { c } = require('c.js');
module.exports = { b, c };
// Output
async function moduleInitFunction(require, exports = {}) {
  const module = { exports };
  await require('init');
  const b = await require('b.js');
  const { c } = await require('c.js');
  module.exports = { b, c };
  return module.exports;
}

Example — requireName

// Input
const b = require('b.js');
// wrapWithAsyncFn(false, { requireName: 'load' })
async function moduleInitFunction(load, exports = {}) {
  const module = { exports };
  const b = await load('b.js');
  return module.exports;
}

When globalRequire: true is combined with a custom requireName, the parameter defaults to the global require:

// wrapWithAsyncFn(true, { requireName: 'load' })
async function moduleInitFunction(load = require, exports = {}) { ... }

Example — convertImports

All five ES6 import forms are converted automatically (enabled by default):

// Input
import defaultExport from 'a';
import { x, y } from 'b';
import * as ns from 'c';
import 'd';
import def, { z } from 'e';
// Output (inside wrapper)
const defaultExport = (await require('a')).default;
const { x, y } = await require('b');
const ns = await require('c');
await require('d');
const _import0 = await require('e');
const def = _import0.default;
const { z } = _import0;

Example — hoistNestedRequires

require() inside nested functions is lifted to the top of the wrapper (enabled by default):

// Input
function process(item) {
  const { helper } = require('helpers');
  return helper(item);
}
// Output (inside wrapper)
const _hoisted0 = await require('helpers');
function process(item) {
  const { helper } = _hoisted0;
  return helper(item);
}

Multiple nested requires each receive a unique variable: _hoisted0, _hoisted1, etc. Hoisted declarations are placed before all other module code.

Dynamic import()

Top-level import() and await import() are both converted to await require(). Nested import() is left as-is since it returns a Promise natively.

import('lazy-module');          // → await require('lazy-module')
await import('lazy-module');    // → await require('lazy-module')

// Nested — left unchanged
const fn = async () => { await import('lazy-module'); };

Generator Wrapper

wrapWithGeneratorFn(async?, options?) wraps the module in a generator function. Every require() and top-level import() call is converted to yield { require: '<name>' }, pausing execution until the caller resumes with the resolved module.

Options

| Argument | Type | Default | Description | |---|---|---|---| | async | boolean | true | When true, produces async function*; when false, produces function*. | | options.convertImports | boolean | true | Converts ES6 import declarations to yield { require: ... }. Set to false to throw on any import declaration instead. | | options.hoistNestedRequires | boolean | true | Hoists require() inside nested functions to the wrapper top. Set to false to throw on nested require() instead. |

Example — basic

// Input
require('init');
const b = require('b.js');
// Output (async=true)
async function* moduleInitFunction(exports = {}) {
  const module = { exports };
  yield { require: 'init' };
  const b = yield { require: 'b.js' };
  return module.exports;
}

Example — async: false

function* moduleInitFunction(exports = {}) {
  const module = { exports };
  yield { require: 'init' };
  const b = yield { require: 'b.js' };
  return module.exports;
}

Example — convertImports

// Input
import foo from 'a';
import { x } from 'b';
// Output (inside wrapper)
const foo = (yield { require: 'a' }).default;
const { x } = yield { require: 'b' };

Dynamic import()

Top-level import() and await import() are both converted to yield { require: ... }. Nested import() is left as-is.

import('lazy-module');          // → yield { require: 'lazy-module' }
await import('lazy-module');    // → yield { require: 'lazy-module' }

Both wrappers — module.exports support

Both wrappers inject const module = { exports } and return module.exports. All three CommonJS export patterns work correctly:

exports.foo = 1;           // ✓
module.exports.foo = 1;    // ✓
module.exports = { foo };  // ✓  (return module.exports picks up the reassignment)

Calling a wrapped module

Async wrapper — custom HTTP loader

const moduleCache = new Map();

const asyncRequire = async (name, exports) => {
  const code = await fetch(`/modules?name=${encodeURIComponent(name)}`).then((r) => r.text());
  eval(code); // moduleInitFunction is now defined
  return moduleInitFunction(asyncRequire, exports);
};

const require = (name) => {
  if (moduleCache.has(name)) return moduleCache.get(name);
  const exports = {};
  // Store early to handle circular dependencies
  moduleCache.set(name, exports);
  return asyncRequire(name, exports);
};

const myModule = await require('entry-module');

Generator wrapper — step-through loader

async function loadModule(name) {
  const code = await fetch(`/modules?name=${encodeURIComponent(name)}`).then((r) => r.text());
  eval(code); // moduleInitFunction is now defined

  const gen = moduleInitFunction();
  let step = gen.next();

  while (!step.done) {
    const depExports = await loadModule(step.value.require);
    step = gen.next(depExports);
  }

  return step.value; // final module.exports
}

Opting out of default behaviour

All conversions are on by default. Pass explicit options to disable any of them and restore strict error-throwing behaviour:

// Throw on import declarations and nested requires instead of converting/hoisting
wrapWithAsyncFn(false, { convertImports: false, hoistNestedRequires: false })
wrapWithGeneratorFn(true, { convertImports: false, hoistNestedRequires: false })

Known limitations

  • AMD/UMD modules — only CommonJS require() and ES6 import are handled.
  • await require() inside nested async functions — the await is preserved; only bare require() calls are hoisted.
  • Nested import() is not hoisted — it returns a Promise natively and is left unchanged.

Running tests

npm install
npm test

To run the transformation demo (prints transformed output to stdout):

npm run test:demo

Live demo

A working example using the async wrapper to load modules over HTTP is available at js-codemirror-package.