Publishing Plugins
Once you’ve built a plugin, you can publish it to the EmDash Marketplace so other sites can install it from the admin dashboard.
Prerequisites
Section titled “Prerequisites”Before publishing, make sure your plugin:
- Has a valid
package.jsonwith the"."export pointing to your plugin entry - Uses
definePlugin()with a uniqueidand valid semverversion - Declares its
capabilities(what APIs it needs access to)
Bundle Format
Section titled “Bundle Format”Published plugins are distributed as .tar.gz tarballs containing:
| File | Required | Description |
|---|---|---|
manifest.json | Yes | Plugin metadata extracted from definePlugin() |
backend.js | No | Bundled sandbox code (self-contained ES module) |
admin.js | No | Bundled admin UI code |
README.md | No | Plugin documentation |
icon.png | No | Plugin icon (256x256 PNG) |
screenshots/ | No | Up to 5 screenshots (PNG/JPEG, max 1920x1080) |
The manifest.json is generated automatically from your definePlugin() call. It contains the plugin ID, version, capabilities, hook names, route names, and admin configuration — but no executable code.
Building a Bundle
Section titled “Building a Bundle”The emdash plugin bundle command produces a tarball from your plugin source:
cd packages/plugins/my-pluginemdash plugin bundleThis will:
- Read your
package.jsonto find entrypoints - Build the main entry with tsdown to extract the manifest
- Bundle
backend.js(minified, tree-shaken, self-contained) - Bundle
admin.jsif an"./admin"export exists - Collect assets (README, icon, screenshots)
- Validate the bundle (size limits, no Node.js built-ins in backend)
- Write
{id}-{version}.tar.gztodist/
Entrypoint Resolution
Section titled “Entrypoint Resolution”The bundle command finds your code through package.json exports:
{ "exports": { ".": { "import": "./dist/index.mjs" }, "./sandbox": { "import": "./dist/sandbox-entry.mjs" }, "./admin": { "import": "./dist/admin.mjs" } }}| Export | Purpose | Built as |
|---|---|---|
"." | Main entry — used to extract the manifest | Externals: emdash, @emdash-cms/* |
"./sandbox" | Backend code that runs in the sandbox | Fully self-contained (no externals) |
"./admin" | Admin UI components | Fully self-contained |
If "./sandbox" is missing, the command looks for src/sandbox-entry.ts as a fallback.
Options
Section titled “Options”emdash plugin bundle [--dir <path>] [--outDir <path>]| Flag | Default | Description |
|---|---|---|
--dir | Current directory | Plugin source directory |
--outDir, -o | dist | Output directory for the tarball |
Validation
Section titled “Validation”The bundle command checks:
- Size limit — Total bundle must be under 5MB
- No Node.js built-ins —
backend.jscannot importfs,path,child_process, etc. (sandbox code runs in a V8 isolate, not Node.js) - Icon dimensions —
icon.pngshould be 256x256 (warns if wrong, still includes it) - Screenshot limits — Max 5 screenshots, max 1920x1080
Publishing
Section titled “Publishing”The emdash plugin publish command uploads your tarball to the marketplace:
emdash plugin publishThis will find the most recent .tar.gz in your dist/ directory and upload it. You can also specify the tarball explicitly or build before publishing:
# Explicit tarball pathemdash plugin publish --tarball dist/my-plugin-1.0.0.tar.gz
# Build first, then publishemdash plugin publish --buildAuthentication
Section titled “Authentication”The first time you publish, the CLI authenticates you via GitHub:
- The CLI opens your browser to GitHub’s device authorization page
- You enter the code displayed in your terminal
- GitHub issues an access token
- The CLI exchanges it for a marketplace JWT (stored in
~/.config/emdash/auth.json)
The token lasts 30 days. After it expires, you’ll be prompted to re-authenticate on the next publish.
You can also manage authentication separately:
# Log in without publishingemdash plugin login
# Log out (clear stored token)emdash plugin logoutFirst-Time Registration
Section titled “First-Time Registration”If your plugin ID doesn’t exist in the marketplace yet, emdash plugin publish registers it automatically before uploading the first version.
Version Requirements
Section titled “Version Requirements”Each published version must have a higher semver than the last. You cannot overwrite or republish an existing version.
Security Audit
Section titled “Security Audit”Every published version goes through an automated security audit. The marketplace scans your backend.js and admin.js for:
- Data exfiltration patterns
- Credential harvesting via settings
- Obfuscated code
- Resource abuse (crypto mining, etc.)
- Suspicious network activity
The audit produces a verdict of pass, warn, or fail, which is displayed on the plugin’s marketplace listing. Depending on the marketplace’s enforcement level, a fail verdict may block publication entirely.
Options
Section titled “Options”emdash plugin publish [--tarball <path>] [--build] [--dir <path>] [--registry <url>]| Flag | Default | Description |
|---|---|---|
--tarball | Latest .tar.gz in dist/ | Explicit tarball path |
--build | false | Run emdash plugin bundle before publishing |
--dir | Current directory | Plugin directory (used with --build) |
--registry | https://marketplace.emdashcms.com | Marketplace URL |
Complete Workflow
Section titled “Complete Workflow”Here’s the typical publish cycle:
# 1. Make your changes# 2. Bump the version in definePlugin() and package.json# 3. Bundle and publish in one stepemdash plugin publish --buildOr if you prefer to inspect the bundle first:
# Build the tarballemdash plugin bundle
# Check the outputtar tzf dist/my-plugin-1.1.0.tar.gz
# Publishemdash plugin publish