
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
@moduul/builder
Advanced tools
CLI tool for building plugins for the Moduul plugin system. Bundles TypeScript source code into optimized JavaScript with automatic manifest handling.
plugin.manifest.json is validnpm install -D @moduul/builder
# Build current directory (defaults: input=./src, output=./dist, format=esm)
moduul build
# Specify input and output directories
moduul build --input src --output dist
# Build as CommonJS
moduul build --format cjs
# Create a ZIP archive for distribution
moduul build --zip
buildBundles a plugin into a distributable format.
moduul build [options]
Options:
-i, --input <path> - Input directory containing plugin source (default: ./src)-o, --output <path> - Output directory for built plugin (default: ./dist)-f, --format <format> - Output module format: esm, cjs, or iife (default: esm)-m, --minify - Minify the output (default: false)--zip - Create a ZIP archive of the built plugin-h, --help - Display helpExamples:
# Default build (ESM output)
moduul build
# Build as CommonJS
moduul build --format cjs
# Custom input/output directories
moduul build --input src --output build
# Build and zip
moduul build --zip
# Minified CJS build with ZIP
moduul build --format cjs --minify --zip
A typical plugin project structure:
my-plugin/
├── src/
│ └── index.ts # Entry point
├── plugin.manifest.json # Plugin manifest
├── package.json
├── tsconfig.json
└── dist/ # Generated after build
├── index.js
├── index.js.map
└── plugin.manifest.json
The plugin.manifest.json is required and must contain:
{
"name": "my-plugin",
"version": "1.0.0",
"entryPoint": "./dist/index.js",
"meta": {
"description": "My awesome plugin",
"author": "Your Name"
}
}
Required Fields:
name - Unique plugin identifier (kebab-case recommended)version - Semantic version (e.g., "1.0.0")entryPoint - Path to the bundled output relative to plugin rootOptional Fields:
meta.description - Human-readable descriptionmeta.author - Author namemeta.* - Any additional metadataThe builder performs these steps:
plugin.manifest.json.map files for debuggingplugin.zipThe builder uses esbuild with these settings:
--format cjs or --format iife--minifyThe builder automatically handles TypeScript:
src/index.ts:
export default {
name: 'my-plugin',
version: '1.0.0',
async execute(): Promise<string> {
return 'Hello from TypeScript!';
}
};
Output (dist/index.js, ESM):
// Compiled and bundled ES module
export default {
name: 'my-plugin',
version: '1.0.0',
async execute() {
return 'Hello from TypeScript!';
}
};
Output (dist/index.js, CJS) — built with --format cjs:
// Compiled and bundled CommonJS module
module.exports = {
name: 'my-plugin',
version: '1.0.0',
async execute() {
return 'Hello from TypeScript!';
}
};
src/index.ts:
export default {
name: 'basic-plugin',
execute() {
console.log('Plugin executed!');
}
};
src/index.ts:
import fetch from 'node-fetch';
export default {
name: 'fetch-plugin',
async execute(url: string) {
const response = await fetch(url);
return await response.json();
}
};
src/index.ts:
let initialized = false;
export default {
name: 'stateful-plugin',
async init() {
console.log('Initializing plugin...');
initialized = true;
},
execute() {
if (!initialized) {
throw new Error('Plugin not initialized');
}
return 'Working!';
}
};
mkdir my-plugin
cd my-plugin
npm init -y
npm install -D @moduul/builder typescript
package.json:
{
"name": "my-plugin",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "moduul build",
"build:cjs": "moduul build --format cjs"
},
"devDependencies": {
"@moduul/builder": "*",
"typescript": "^5.0.0"
}
}
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true
},
"include": ["src/**/*"]
}
plugin.manifest.json:
{
"name": "my-plugin",
"version": "1.0.0",
"entryPoint": "./dist/index.js",
"meta": {
"description": "My plugin"
}
}
src/index.ts:
export default {
name: 'my-plugin',
execute() {
return 'Hello!';
}
};
npm run build
import { PluginHost } from '@moduul/core';
const host = new PluginHost({ folder: './my-plugin' });
await host.reload();
const plugin = host.find('my-plugin');
console.log(await plugin.plugin.execute());
Copy the dist/ folder or plugin.zip to your plugins directory:
cp -r dist/ /path/to/plugins/my-plugin/
# or
cp plugin.zip /path/to/plugins/
You can publish plugins to npm:
npm publish dist/
Users can then install and use:
npm install your-plugin
# ESM output (default)
moduul build --format esm
# CommonJS output (required by some host environments)
moduul build --format cjs
# IIFE output (browser-compatible self-executing bundle)
moduul build --format iife
# Custom input/output paths
moduul build --input src --output build
Build multiple variants:
# Development ESM build
moduul build --output dist-dev
# Production CJS build with zip
moduul build --format cjs --output dist-prod --zip
package.json:
{
"scripts": {
"build": "moduul build",
"build:cjs": "moduul build --format cjs",
"build:prod": "moduul build --minify --zip",
"clean": "rm -rf dist",
"prepublishOnly": "npm run build:prod"
}
}
Ensure your package.json has "type": "module":
{
"type": "module"
}
Check that your plugin.manifest.json has all required fields:
name (string)version (string)entryPoint (string, relative path)Verify the path specified in --entry exists:
ls src/index.ts # Should exist
You can also use the builder programmatically:
import { buildPlugin, watchPlugin } from '@moduul/builder';
// Build once
await buildPlugin({
input: './src',
output: './dist',
format: 'cjs', // 'esm' | 'cjs' | 'iife' — defaults to 'esm'
minify: false,
zip: false,
});
// Watch mode
await watchPlugin({
input: './src',
output: './dist',
format: 'esm',
});
MIT
FAQs
CLI tool for building plugins for the Moduul plugin system
We found that @moduul/builder demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.