Skip to content

Bundling and packaging

After creating your universal middleware, you need to bundle it before publishing.

You can use either rollup-based tools (such as rollup or vite) or esbuild-based tools (like esbuild or tsup) for bundling.

Bundling

ts
import { rollup } from "rollup";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";
import universalMiddleware from "universal-middleware/rollup";

// See https://github.com/magne4000/universal-middleware/blob/main/packages/universal-middleware/test/rollup.test.ts
// for a list of possible options and usage
const result = await rollup({
  // Your middlewares and handlers must all be added as input
  input: "./src/middleware/demo.middleware.ts",
  plugins: [
    universalMiddleware(),
    // usually required
    nodeResolve(),
    // required if using typescript
    typescript(),
  ]
});
ts
// vite.config.js

import { resolve } from "node:path";
import { defineConfig } from "vite";
import universalMiddleware from "universal-middleware/rollup";

export default defineConfig({
  plugins: [universalMiddleware()],
  build: {
    lib: {
      // Could also be a dictionary or array of multiple entry points
      entry: resolve(__dirname, 'src/middleware/demo.middleware.ts'),
      name: 'My Awesome Middleware',
      fileName: 'some-lib',
    }
  },
});
ts
import { build } from "esbuild";
import universalMiddleware from "universal-middleware/esbuild";

// See https://github.com/magne4000/universal-middleware/blob/main/packages/universal-middleware/test/esbuild.test.ts
// for a list of possible options and usage
const result = await build({
  entryPoints: ["./src/middleware/demo.middleware.ts"],
  plugins: [
    universalMiddleware(),
  ],
  outdir: "dist",
  bundle: true,
  platform: "neutral",
  format: "esm",
  target: "es2022",
  splitting: true,
});
ts
// tsup.config.ts

import { defineConfig } from "tsup";
import universalMiddleware from "universal-middleware/esbuild";

export default defineConfig([
  {
    entry: {
      "middlewares/demo": "./src/middlewares/demo.middleware.ts",
    },
    format: ["esm"],
    platform: "neutral",
    target: "es2022",
    esbuildPlugins: [universalMiddleware()],
    esbuildOptions(opts) {
      opts.outbase = "src";
    },
    bundle: true,
  },
]);
Plugin options

The bundler plugin also accepts the following options:

ts
universalMiddleware({
  // Only generate files for selected servers. All enabled by default
  servers?: ('hono' | 'express' | 'hattip' | 'fastify' | 'h3' | 'webroute' | 'cloudflare-pages' | 'cloudflare-worker' | 'elysia')[];
  // akin to esbuild `entryNames` for generated "exports" in package.json
  serversExportNames?: string;
  // akin to ebsuild `entryNames` for generated "exports" in package.json
  entryExportNames?: string;
  // Disables some warning
  ignoreRecommendations?: boolean;
  // No auto writing in package.json. All info available to do it manually in `buildEnd`
  doNotEditPackageJson?: boolean;
  // Generate typings. true by default
  dts?: boolean;
  // Hook called when bundle is generated
  // For details, check https://github.com/magne4000/universal-middleware/blob/main/packages/universal-middleware/src/plugin.ts
  buildEnd?: (report: Report[]) => void | Promise<void>;
});

Building

Once the build is executed, your dist folder will contain the following files:

dist
└─ middlewares
  # raw middleware, compiled to js + types
  ├─ demo.d.ts
  ├─ demo.js
  # compiled adapters + types
  ├─ universal-express-middleware-demo.middleware.d.ts
  ├─ universal-express-middleware-demo.middleware.js
  ├─ universal-h3-middleware-demo.middleware.d.ts
  ├─ universal-h3-middleware-demo.middleware.js
  ├─ universal-hono-middleware-demo.middleware.d.ts
  ├─ universal-hono-middleware-demo.middleware.js
  └─ ...

Your project's package.json will be updated to contains the necessary exports and optionalDependencies.

Details
json5
{
  // ...
  "exports": {
    "./middlewares/demo-middleware": {
      "types": "./dist/middlewares/demo.d.ts",
      "import": "./dist/middlewares/demo.js",
      "default": "./dist/middlewares/demo.js"
    },
    "./middlewares/demo-middleware-hono": {
      "types": "./dist/middlewares/universal-hono-middleware-demo.middleware.d.ts",
      "import": "./dist/middlewares/universal-hono-middleware-demo.middleware.js",
      "default": "./dist/middlewares/universal-hono-middleware-demo.middleware.js"
    },
    "./middlewares/demo-middleware-express": {
      "types": "./dist/middlewares/universal-express-middleware-demo.middleware.d.ts",
      "import": "./dist/middlewares/universal-express-middleware-demo.middleware.js",
      "default": "./dist/middlewares/universal-express-middleware-demo.middleware.js"
    },
    "./middlewares/demo-middleware-h3": {
      "types": "./dist/middlewares/universal-h3-middleware-demo.middleware.d.ts",
      "import": "./dist/middlewares/universal-h3-middleware-demo.middleware.js",
      "default": "./dist/middlewares/universal-h3-middleware-demo.middleware.js"
    },
    // ...
  },
  "optionalDependencies": {
    "@universal-middleware/express": "^0",
    "@universal-middleware/h3": "^0",
    "@universal-middleware/hono": "^0",
    // ...
  }
}

TIP

You can opt out of this behaviour by setting doNotEditPackageJson: true in the plugin options.

Publishing

Your package should now be ready to be published! 🚀