{"_id":"@actualwave/babel-ioc-dep-wrap-plugin","name":"@actualwave/babel-ioc-dep-wrap-plugin","dist-tags":{"latest":"0.0.4"},"versions":{"0.0.4":{"name":"@actualwave/babel-ioc-dep-wrap-plugin","version":"0.0.4","description":"Babel plugin that wraps module into container to provide dependencies asynchronously from custom sources.","main":"index.js","exports":{".":"./index.js"},"scripts":{"test":"NODE_OPTIONS=--experimental-vm-modules jest","test:demo":"cd ./test && node index.js"},"type":"module","author":{"name":"Oleg Galaburda","email":"burdiuz@gmail.com"},"license":"ISC","devDependencies":{"@babel/core":"^7.15.5","@jest/globals":"^29.0.0","@types/babel__core":"^7.1.16","jest":"^29.0.0"},"peerDependencies":{"@babel/core":"*","@babel/traverse":"*"},"_id":"@actualwave/babel-ioc-dep-wrap-plugin@0.0.4","gitHead":"a608f54f0bfb0cc6ebabab65a0374c9fed65dc89","_nodeVersion":"24.6.0","_npmVersion":"11.5.1","dist":{"integrity":"sha512-e63MMTFyGevCwVTywbiQudWTaWIf2yt21fWumaQXiwd7A2BsX/DasuBwFTjtG3cy9OKDHBN27j+4xYteEWG33g==","shasum":"a7aea8536ed9bc233c10e92e26bbb325b063375e","tarball":"https://registry.npmjs.org/@actualwave/babel-ioc-dep-wrap-plugin/-/babel-ioc-dep-wrap-plugin-0.0.4.tgz","fileCount":8,"unpackedSize":34511,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBH+oqPRX5M03cC5U728qU+QsXDXIDM6ewWuSykf3YjPAiEAqje/dHpvSmabD52rrS3CzEzm1QfN3eB8D2AKgb1poZw="}]},"_npmUser":{"name":"actualwave","email":"burdiuz@gmail.com"},"directories":{},"maintainers":[{"name":"actualwave","email":"burdiuz@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/babel-ioc-dep-wrap-plugin_0.0.4_1780073054105_0.13921729158709617"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-29T16:44:13.978Z","0.0.4":"2026-05-29T16:44:14.238Z","modified":"2026-05-29T16:44:14.428Z"},"maintainers":[{"name":"actualwave","email":"burdiuz@gmail.com"}],"description":"Babel plugin that wraps module into container to provide dependencies asynchronously from custom sources.","author":{"name":"Oleg Galaburda","email":"burdiuz@gmail.com"},"license":"ISC","readme":"# Babel IoC Dependency Wrapper Plugin\n\nWraps 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.\n\nBoth wrappers handle ES6 `import` declarations, dynamic `import()` calls, and `require()` calls inside nested functions out of the box — all conversions are enabled by default.\n\n## Installation\n\n```bash\nnpm install --save-dev @actualwave/babel-ioc-dep-wrap-plugin\n```\n\n`@babel/core` is required as a peer dependency:\n\n```bash\nnpm install --save-dev @babel/core\n```\n\n## Usage\n\nBoth plugins are plain Babel plugin factories — call them to get a plugin and pass the result in the `plugins` array.\n\n```javascript\nimport babel from '@babel/core';\nimport { wrapWithAsyncFn, wrapWithGeneratorFn } from '@actualwave/babel-ioc-dep-wrap-plugin';\n\nconst result = babel.transformSync(sourceCode, {\n  plugins: [wrapWithAsyncFn()],\n});\n```\n\n---\n\n## Async Wrapper\n\n`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.\n\n### Options\n\n| Argument | Type | Default | Description |\n|---|---|---|---|\n| `globalRequire` | `boolean` | `false` | When `true`, the resolver parameter defaults to the global `require`, allowing fallback to normal Node.js resolution. |\n| `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. |\n| `options.convertImports` | `boolean` | `true` | Converts ES6 `import` declarations to `await <requireName>()` calls automatically. Set to `false` to throw on any `import` declaration instead. |\n| `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. |\n\n### Example — basic\n\n```javascript\n// Input\n\"use strict\";\nrequire('init');\nconst b = require('b.js');\nconst { c } = require('c.js');\nmodule.exports = { b, c };\n```\n\n```javascript\n// Output\nasync function moduleInitFunction(require, exports = {}) {\n  const module = { exports };\n  await require('init');\n  const b = await require('b.js');\n  const { c } = await require('c.js');\n  module.exports = { b, c };\n  return module.exports;\n}\n```\n\n### Example — `requireName`\n\n```javascript\n// Input\nconst b = require('b.js');\n```\n\n```javascript\n// wrapWithAsyncFn(false, { requireName: 'load' })\nasync function moduleInitFunction(load, exports = {}) {\n  const module = { exports };\n  const b = await load('b.js');\n  return module.exports;\n}\n```\n\nWhen `globalRequire: true` is combined with a custom `requireName`, the parameter defaults to the global `require`:\n\n```javascript\n// wrapWithAsyncFn(true, { requireName: 'load' })\nasync function moduleInitFunction(load = require, exports = {}) { ... }\n```\n\n### Example — `convertImports`\n\nAll five ES6 import forms are converted automatically (enabled by default):\n\n```javascript\n// Input\nimport defaultExport from 'a';\nimport { x, y } from 'b';\nimport * as ns from 'c';\nimport 'd';\nimport def, { z } from 'e';\n```\n\n```javascript\n// Output (inside wrapper)\nconst defaultExport = (await require('a')).default;\nconst { x, y } = await require('b');\nconst ns = await require('c');\nawait require('d');\nconst _import0 = await require('e');\nconst def = _import0.default;\nconst { z } = _import0;\n```\n\n### Example — `hoistNestedRequires`\n\n`require()` inside nested functions is lifted to the top of the wrapper (enabled by default):\n\n```javascript\n// Input\nfunction process(item) {\n  const { helper } = require('helpers');\n  return helper(item);\n}\n```\n\n```javascript\n// Output (inside wrapper)\nconst _hoisted0 = await require('helpers');\nfunction process(item) {\n  const { helper } = _hoisted0;\n  return helper(item);\n}\n```\n\nMultiple nested requires each receive a unique variable: `_hoisted0`, `_hoisted1`, etc. Hoisted declarations are placed before all other module code.\n\n### Dynamic `import()`\n\nTop-level `import()` and `await import()` are both converted to `await require()`. Nested `import()` is left as-is since it returns a Promise natively.\n\n```javascript\nimport('lazy-module');          // → await require('lazy-module')\nawait import('lazy-module');    // → await require('lazy-module')\n\n// Nested — left unchanged\nconst fn = async () => { await import('lazy-module'); };\n```\n\n---\n\n## Generator Wrapper\n\n`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.\n\n### Options\n\n| Argument | Type | Default | Description |\n|---|---|---|---|\n| `async` | `boolean` | `true` | When `true`, produces `async function*`; when `false`, produces `function*`. |\n| `options.convertImports` | `boolean` | `true` | Converts ES6 `import` declarations to `yield { require: ... }`. Set to `false` to throw on any `import` declaration instead. |\n| `options.hoistNestedRequires` | `boolean` | `true` | Hoists `require()` inside nested functions to the wrapper top. Set to `false` to throw on nested `require()` instead. |\n\n### Example — basic\n\n```javascript\n// Input\nrequire('init');\nconst b = require('b.js');\n```\n\n```javascript\n// Output (async=true)\nasync function* moduleInitFunction(exports = {}) {\n  const module = { exports };\n  yield { require: 'init' };\n  const b = yield { require: 'b.js' };\n  return module.exports;\n}\n```\n\n### Example — `async: false`\n\n```javascript\nfunction* moduleInitFunction(exports = {}) {\n  const module = { exports };\n  yield { require: 'init' };\n  const b = yield { require: 'b.js' };\n  return module.exports;\n}\n```\n\n### Example — `convertImports`\n\n```javascript\n// Input\nimport foo from 'a';\nimport { x } from 'b';\n```\n\n```javascript\n// Output (inside wrapper)\nconst foo = (yield { require: 'a' }).default;\nconst { x } = yield { require: 'b' };\n```\n\n### Dynamic `import()`\n\nTop-level `import()` and `await import()` are both converted to `yield { require: ... }`. Nested `import()` is left as-is.\n\n```javascript\nimport('lazy-module');          // → yield { require: 'lazy-module' }\nawait import('lazy-module');    // → yield { require: 'lazy-module' }\n```\n\n---\n\n## Both wrappers — `module.exports` support\n\nBoth wrappers inject `const module = { exports }` and return `module.exports`. All three CommonJS export patterns work correctly:\n\n```javascript\nexports.foo = 1;           // ✓\nmodule.exports.foo = 1;    // ✓\nmodule.exports = { foo };  // ✓  (return module.exports picks up the reassignment)\n```\n\n---\n\n## Calling a wrapped module\n\n### Async wrapper — custom HTTP loader\n\n```javascript\nconst moduleCache = new Map();\n\nconst asyncRequire = async (name, exports) => {\n  const code = await fetch(`/modules?name=${encodeURIComponent(name)}`).then((r) => r.text());\n  eval(code); // moduleInitFunction is now defined\n  return moduleInitFunction(asyncRequire, exports);\n};\n\nconst require = (name) => {\n  if (moduleCache.has(name)) return moduleCache.get(name);\n  const exports = {};\n  // Store early to handle circular dependencies\n  moduleCache.set(name, exports);\n  return asyncRequire(name, exports);\n};\n\nconst myModule = await require('entry-module');\n```\n\n### Generator wrapper — step-through loader\n\n```javascript\nasync function loadModule(name) {\n  const code = await fetch(`/modules?name=${encodeURIComponent(name)}`).then((r) => r.text());\n  eval(code); // moduleInitFunction is now defined\n\n  const gen = moduleInitFunction();\n  let step = gen.next();\n\n  while (!step.done) {\n    const depExports = await loadModule(step.value.require);\n    step = gen.next(depExports);\n  }\n\n  return step.value; // final module.exports\n}\n```\n\n---\n\n## Opting out of default behaviour\n\nAll conversions are on by default. Pass explicit options to disable any of them and restore strict error-throwing behaviour:\n\n```javascript\n// Throw on import declarations and nested requires instead of converting/hoisting\nwrapWithAsyncFn(false, { convertImports: false, hoistNestedRequires: false })\nwrapWithGeneratorFn(true, { convertImports: false, hoistNestedRequires: false })\n```\n\n---\n\n## Known limitations\n\n- **AMD/UMD modules** — only CommonJS `require()` and ES6 `import` are handled.\n- **`await require()` inside nested async functions** — the `await` is preserved; only bare `require()` calls are hoisted.\n- **Nested `import()` is not hoisted** — it returns a Promise natively and is left unchanged.\n\n---\n\n## Running tests\n\n```bash\nnpm install\nnpm test\n```\n\nTo run the transformation demo (prints transformed output to stdout):\n\n```bash\nnpm run test:demo\n```\n\n---\n\n## Live demo\n\nA working example using the async wrapper to load modules over HTTP is available at [js-codemirror-package](https://burdiuz.github.io/js-codemirror-package/).\n","readmeFilename":"README.md","_rev":"1-4e2d2b28e7d40d99a82b5f3d6a03ad80"}