Skip to content

Plugins

Write custom checker rules and share them as npm packages.

Writing a Plugin

A plugin exports an array of rules:

typescript
// my-plugin.ts
import type { Rule, RawDiagnostic } from "tspub";

const noInternalExports: Rule = {
  meta: {
    id: "custom/no-internal-exports",
    description: "Disallow exporting internal modules",
    defaultSeverity: "error",
    fixable: false,
    category: "custom",
  },
  check(ctx) {
    const results: RawDiagnostic[] = [];
    if (ctx.pkg.exports && typeof ctx.pkg.exports === "object") {
      for (const key of Object.keys(ctx.pkg.exports)) {
        if (key.includes("internal")) {
          results.push({
            severity: "error",
            message: `Subpath "${key}" exposes internal module`,
          });
        }
      }
    }
    return results;
  },
};

export const rules = [noInternalExports];

Plugin Structure

A plugin must export one of:

typescript
// Named export
export const rules: Rule[] = [/* ... */];

// Default export
export default { rules: [/* ... */] };

Rule Interface

typescript
interface Rule {
  meta: {
    id: string;          // "category/name" format
    description: string;
    defaultSeverity: "error" | "warning" | "info";
    fixable: false | "safe" | "unsafe";
    category: string;    // Must match id prefix
  };
  check(ctx: CheckContext): RawDiagnostic[] | Promise<RawDiagnostic[]>;
  fix?(ctx: FixContext): FixResult | Promise<FixResult>;
}

CheckContext

Available in the check function:

typescript
interface CheckContext {
  pkg: PackageJson;                          // Parsed package.json
  dir: string;                               // Package directory
  compilerOptions: Record<string, unknown>;   // tsconfig compilerOptions
  hasBuildOutput: boolean;                    // Whether dist/ exists
  distFiles: string[];                       // Files in dist/
  allJsFiles: string[];                      // All .js files
}

Loading Plugins

In tspub.config.ts:

typescript
export default {
  check: {
    plugins: [
      // npm package
      "tspub-plugin-my-rules",

      // Local file (relative to project root)
      "./tools/custom-rules.js",

      // Absolute path
      "/path/to/plugin.js",
    ],
  },
};

Publishing a Plugin

Convention: name your package tspub-plugin-*.

json
{
  "name": "tspub-plugin-my-rules",
  "type": "module",
  "exports": {
    ".": "./dist/index.js"
  }
}

Fixable Rules

Plugins can provide auto-fix:

typescript
const myRule: Rule = {
  meta: { /* ... */ fixable: "safe" },
  check(ctx) { /* ... */ },
  fix(ctx) {
    ctx.pkg.sideEffects = false;
    return { message: "added sideEffects: false", pkgModified: true };
  },
};
  • "safe" — applied with --fix
  • "unsafe" — only applied with --fix --unsafe

Released under the MIT License.