Documentation / Développement / Guides pratiques / Build and bundle a plugin

Build and bundle a plugin

meridian-plugin build bundles your plugin into a dist/ that the host can load without any npm install — runtime dependencies are inlined into the emitted JavaScript. This recipe runs the build and shows how to confirm the artifact is self-contained. For the reasoning behind the bundle boundary, see Bundling and distribution; for the command's flags, see the CLI reference.

Prerequisites

  • A plugin project that already compiles — scaffolded per Scaffold a plugin or an equivalent layout, with a plugin.yaml and the modules it references in place.
  • Dependencies installed (npm install), including any runtime packages your behaviors import.

Run the build

  1. From the plugin root, run the build:

    npm run build          # → meridian-plugin build (the scaffold wires this up)
    

    Equivalently, invoke the CLI directly: meridian-plugin build [dir=.].

  2. Read the summary line. On success the CLI reports the plugin, its version, and the number of bundled modules, and exits zero:

    ✓ @acme/vitals@0.1.0 → dist/ (1 module(s) bundlé(s), deps runtime incluses).
    

    A non-zero exit means a load check, esbuild or the declaration emit failed; the error names the problem, the module or the tsconfig at fault.

What the build produces

meridian-plugin build (libs/plugin-cli/src/commands/build.ts) first checks the plugin, then wipes and recreates dist/:

  • Checks the plugin and its dependsOn closure with the host's load checks (manifest keys the schema does not know, dependsOn ranges against the versions found, declared events, event: triggers, the defaultSelector key ignored on a node, bindings, sdk range; see Load checks). A dependency found neither in node_modules nor in a sibling directory is absent, a problem of the built plugin, with a line that says where the CLI looked; for a dependency refused, the problems that refuse it are printed under dépendances : <n> note(s) de refus :. meridian-plugin deps lists each dependency with the version found and its state. A problem of the built plugin itself is printed as - <plugin> › <name> (<kind>) : <message>, and the build stops with ✗ <name> : <n> problème(s) de chargement, dist/ non écrit. before anything is written: a published version is immutable. An unknown key is named by its path; a value cut by an unquoted comma in a { … } mapping reads, for example:

      - @acme/vitals › contributes.nodes[0].outputs[1]["émis si l'événement le porte."] (key) : clé inconnue, ignorée : la valeur de « description » est coupée à une virgule. Mettre cette valeur entre guillemets.
    
  • Bundles every module referenced by the manifest — each node behavior, receive and assist module, each adapter, each eventSource, each message binding and each port sandbox module: — with esbuild (format: esm, platform: node, target: node20, splitting: true). Your plugin's runtime dependencies are inlined into the output, so the published artifact carries no node_modules of its own. Code shared between modules is factored into dist/chunks/.

  • Leaves two import families external, because the host guarantees them: Node builtins (node:*) and the SDK (@meridian/*). These are never inlined.

  • Emits TypeScript declarations when the project has a tsconfig.build.json or tsconfig.json (tsc --emitDeclarationOnly --noCheck). These back a plugin's ./types export, consumed by any plugin that declares a dependsOn on yours. A project without a tsconfig simply emits none.

  • Copies plugin.yaml into dist/, rewriting every module: path from .ts to .js so it points at the bundled output.

  • Copies i18n/*.yaml into dist/i18n/, where the host reads them next to the manifest.

dist/ is exactly what you ship — there is no separate packaging step.

Cross-plugin type imports

Imports of a dependency's types (@scope/dep/types) are import type and erased at compile time, so they never enter the runtime bundle. They only need to resolve during your own type-check and declaration emit. See Generate types and Add vocabulary and types.

Verify the artifact is self-contained

  1. Inspect the tree — you should see the bundled JS, the rewritten manifest, and any i18n and declarations:

    ls -R dist
    
  2. Confirm your runtime dependencies were inlined rather than left as bare imports. The only imports that may remain in the emitted JS are node:* and @meridian/*:

    grep -rE "from \"[^.@]" dist --include="*.js"        # bare (non-relative) imports
    

    Every hit should be either a node: builtin or an @meridian/ sub-path. A package name from your dependencies appearing here means it was not bundled — see the caveat below.

Caveat: native dependencies cannot be bundled

A bundler inlines JavaScript; it cannot inline a native (compiled) npm dependency — anything shipping a .node binary or a build step. Avoid such packages in plugins. If one is genuinely required, it cannot travel in dist/ and must instead be provided by the host image, which puts the burden back on the operator and breaks the drop-in autonomy a bundled plugin is meant to have.

Next steps

97 documents8 sectionssource : /docs · généré au build