npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ttsc/unplugin

v0.21.0

Published

Bundler adapters for ttsc plugins.

Readme

@ttsc/unplugin

banner of @ttsc/unplugin

GitHub license NPM Version NPM Downloads Build Status Guide Documents Discord Badge

Bundler adapter for ttsc plugins.

Use it when Vite, Rollup, esbuild, Webpack, Rspack, Next.js, Farm, or Bun owns the build but the project still needs ttsc plugins.

Setup

Install ttsc and TypeScript-Go first. Then install the bundler adapter:

npm install -D ttsc typescript
npm install -D @ttsc/unplugin

Choose your bundler and add the adapter.

Vite

// vite.config.ts
import ttsc from "@ttsc/unplugin/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [ttsc()],
});

Rollup

// rollup.config.ts
import ttsc from "@ttsc/unplugin/rollup";

export default {
  input: "src/index.ts",
  output: {
    dir: "dist",
    format: "esm",
  },
  plugins: [ttsc()],
};

Rolldown

// rolldown.config.ts
import ttsc from "@ttsc/unplugin/rolldown";

export default {
  input: "src/index.ts",
  output: {
    dir: "dist",
    format: "esm",
  },
  plugins: [ttsc()],
};

esbuild

// esbuild.config.ts
import { build } from "esbuild";
import ttsc from "@ttsc/unplugin/esbuild";

await build({
  entryPoints: ["src/index.ts"],
  outdir: "dist",
  bundle: true,
  plugins: [ttsc()],
});

Webpack

// webpack.config.mjs
import ttsc from "@ttsc/unplugin/webpack";

export default {
  entry: "./src/index.ts",
  output: {
    path: new URL("./dist", import.meta.url).pathname,
  },
  plugins: [ttsc()],
};

Rspack

// rspack.config.mjs
import ttsc from "@ttsc/unplugin/rspack";

export default {
  entry: "./src/index.ts",
  plugins: [ttsc()],
};

Next.js

// next.config.mjs
import withTtsc from "@ttsc/unplugin/next";

/** @type {import("next").NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};
export default withTtsc(nextConfig);

withTtsc covers Next.js builds that go through webpack. When Next.js runs on Turbopack (next dev --turbopack), wire the Turbopack loader rules below instead.

Turbopack

Turbopack has no JS plugin API, but it runs webpack loaders through turbopack.rules, and a ttsc transform is exactly loader-shaped (TypeScript source in, transformed source out). @ttsc/unplugin/turbopack is that standalone loader:

// next.config.mjs
/** @type {import("next").NextConfig} */
const nextConfig = {
  turbopack: {
    rules: {
      "*.ts": { loaders: ["@ttsc/unplugin/turbopack"] },
      "*.tsx": { loaders: ["@ttsc/unplugin/turbopack"] },
    },
  },
};
export default nextConfig;

Pass options through the rule's options object: { loader: "@ttsc/unplugin/turbopack", options: { project: "tsconfig.build.json" } }. The loader keeps a per-worker compiler cache (Turbopack runs loaders in a worker pool) and returns the source unchanged for declaration files, node_modules paths, and transforms that produce no change.

Farm

// farm.config.ts
import ttsc from "@ttsc/unplugin/farm";
import { defineConfig } from "@farmfe/core";

export default defineConfig({
  plugins: [ttsc()],
});

Bun

// build.ts
import ttsc from "@ttsc/unplugin/bun";

await Bun.build({
  entrypoints: ["src/index.ts"],
  outdir: "dist",
  plugins: [ttsc()],
});

Under Bun.build, the adapter yields to the next loader for declarations, node_modules, source that ttsc leaves unchanged, and entries supplied through Bun.build({ files }). In-memory entries remain with Bun because ttsc transforms filesystem-backed project inputs. Bun's runtime onLoad contract does not accept an undefined result, so Bun.plugin() explicitly passes excluded and unchanged filesystem files through with their original source. Bun.build clears the project generation through its onStart lifecycle on every build. The runtime API has no corresponding hook, so one setup is treated as one immutable module-loading session: restart the Bun process after changing source, tsconfig, or plugin inputs.

Configuration

By default, @ttsc/unplugin finds the nearest tsconfig.json from the file being transformed and uses that project's plugin settings, including directly installed plugin packages.

If that is already the config you want, ttsc() is enough.

Project Selection

Use project when the bundler should read a different config file:

import ttsc from "@ttsc/unplugin/vite";

export default {
  plugins: [
    ttsc({
      project: "tsconfig.bundle.json",
    }),
  ],
};

The project path is resolved from process.cwd().

Inline Compiler Options

Use compilerOptions when the bundler needs a small override without another config file:

import ttsc from "@ttsc/unplugin/vite";

export default {
  plugins: [
    ttsc({
      compilerOptions: {
        plugins: [
          {
            transform: "@ttsc/lint",
            configFile: "./lint.config.ts",
          },
          {
            transform: "typia/lib/transform",
            finite: true,
          },
        ],
      },
    }),
  ],
};

compilerOptions is layered on top of the selected project config. Existing settings stay in place, and only the fields you pass here are changed for the bundler build.

Plugin-owned keys (typia's finite here) are forwarded to the plugin verbatim. @ttsc/lint reads only configFile from its entry; rules live in the referenced lint.config.* file (or one discovered by upward walk from the tsconfig directory when configFile is absent).

Plugin Overrides

Use the top-level plugins option inside ttsc(...) when the bundler should use a different plugin list from tsconfig.json:

import ttsc from "@ttsc/unplugin/vite";

export default {
  plugins: [
    ttsc({
      plugins: [
        { transform: "@ttsc/lint", configFile: "./lint.config.ts" },
        { transform: "typia/lib/transform" },
      ],
    }),
  ],
};

Explicit adapter options override the plugin list read from the selected project config.

Set plugins: false to run the adapter without loading project plugins.

Next.js Options

Pass adapter options as the second argument:

// next.config.mjs
import withTtsc from "@ttsc/unplugin/next";

/** @type {import("next").NextConfig} */
const nextConfig = {
  reactStrictMode: true,
};

export default withTtsc(nextConfig, {
  project: "tsconfig.bundle.json",
});

Adapter Entrypoints

Import the entrypoint that matches your bundler:

import ttsc from "@ttsc/unplugin/vite";

Supported entrypoints are:

  • @ttsc/unplugin/vite
  • @ttsc/unplugin/esbuild
  • @ttsc/unplugin/rollup
  • @ttsc/unplugin/rolldown
  • @ttsc/unplugin/webpack
  • @ttsc/unplugin/rspack
  • @ttsc/unplugin/turbopack
  • @ttsc/unplugin/farm
  • @ttsc/unplugin/next
  • @ttsc/unplugin/bun

Each entrypoint supports ESM import and CJS require. In CommonJS configs, read the default export from require("@ttsc/unplugin/vite").default. @ttsc/unplugin/turbopack is not an unplugin factory but a standalone webpack loader; reference it by module name inside turbopack.rules instead of calling it.

Options

import type { TtscUnpluginOptions } from "@ttsc/unplugin";

const options: TtscUnpluginOptions = {
  project: "tsconfig.json",
  compilerOptions: {
    baseUrl: ".",
  },
  plugins: false,
};
  • project: path to the tsconfig.json used by the bundler.
  • compilerOptions: temporary override layered on the selected project config.
  • plugins: direct ttsc plugin list override, or false to disable plugins.

Cache and Watch Invalidation

The transform graph also records module-resolution candidates that would outrank a selected target. The adapter registers candidates for importers in the transformed file's reachability closure, so creating or changing a higher-priority probe invalidates watch and persistent caches without an importer or tsconfig edit.

The transform host reports the program's reference graph (the transform envelope's graph section: per-file direct resolved references with type-only edges included, global-scope files, and the tsconfig extends chain — see the plugin protocol). Per transformed file, the adapter registers the graph's reachability closure, the globals, and the configs with the bundler via addWatchFile, so editing a type that only a generated validator depends on invalidates the module — in watch mode, in webpack's filesystem cache, and in Turbopack's fileDependencies — even though the bundler erased the type-only import from its own graph.

Transform plugins may additionally report, per file, the source files they consulted (the envelope's dependencies field); the adapter registers those as watch files too, union semantics. A plugin that declares such a list complete for a file narrows the registration instead: only its own list plus the tsconfig chain, so files the transform never consulted stop invalidating it. Files a plugin declares volatile (output depending on non-file inputs such as environment or time) bypass the adapter's transform cache and are marked uncacheable where the bundler exposes that control.

The transform cache uses the same input set: every regular file reached by the non-following project walk plus graph-reported inputs outside that walk (node_modules declarations, monorepo sibling sources, files reached through symlinks or Windows junctions, and out-of-root extends ancestry). One whole-project compile already contains every module's output. Adapters with a guaranteed build boundary clear the previous generation there, then check only the supplied source on each module's first delivery in that build; a repeated delivery validates the complete snapshots. This avoids one complete project walk per module on an initial build. Bun runtime setup defines one process-scoped loading session. Long-lived hosts without either boundary (Metro workers, the Turbopack loader, and Vite's development server, whose initial buildStart spans later HMR edits) validate the complete project and external-input snapshots on every generation hit.

Sponsors

Sponsors

Thanks for your support.

Your donation encourages ttsc development.

References

Inspired by @ryoppippi/unplugin-typia.