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

kotlified-ts

v0.1.0

Published

Compile-time Kotlin extension functions (let/apply/run/also/takeIf/takeUnless) for Vite — zero prototype pollution.

Readme

kotlified-ts

Compile-time Kotlin extension functions for Vite projects: let, apply, run, also, takeIf, takeUnless.

Write Kotlin-style scope functions in TypeScript/JavaScript, and the plugin rewrites the calls into direct runtime helper invocations at build time. Nothing is ever added to Object.prototype — the let/apply/... you call are compiled away, not monkey-patched in.

// source
const label = users
    .filter((u) => u.active)
    .let((list) => list.map((u) => u.name).join(", "))
    .also(console.log);

const el = document.querySelector("#app").apply((node) => {
    node.dataset.ready = "1";
});
// output (approximately — only the matched calls are replaced)
import {
    letExt as __kt$let,
    alsoExt as __kt$also,
    applyExt as __kt$apply,
} from "kotlified-ts/runtime";

const label = __kt$also(
    __kt$let(
        users.filter((u) => u.active),
        (list) => list.map((u) => u.name).join(", ")
    ),
    console.log
);

const el = __kt$apply(document.querySelector("#app"), (node) => {
    node.dataset.ready = "1";
});

Install

pnpm add -D kotlified-ts
// vite.config.ts
import { defineConfig } from "vite";
import kotlify from "kotlified-ts";

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

Editor types

The plugin ships a global type augmentation so the calls type-check on any value with proper this-polymorphic block parameters:

{
    "compilerOptions": {
        "types": ["kotlified-ts/global"]
    }
}

Supported functions

| Call | Receiver | Block receives | Returns | | ----------------------------- | -------- | -------------- | ---------------------- | | value.let(block) | — | it | block result | | value.apply(block) | this | — | value | | value.run(block) | this | — | block result | | value.also(block) | — | it | value | | value.takeIf(predicate) | — | it | value or undefined | | value.takeUnless(predicate) | — | it | value or undefined |

value?.let(block) and value.let?.(block) use null-safe helpers — the block is skipped when the receiver is null/undefined. A chain like a?.b.let(block) does not: the short-circuit lives inside the receiver, so the plain let helper is used (matching Kotlin).

How it works

  1. transform hook runs enforce: 'pre' on .ts/.tsx/.js/.jsx/.mjs/.cjs/.mts/.cts files and SFC script blocks (.vue/.svelte/.astro type=script).
  2. A fast regex pre-check skips files without candidate calls, then the file is parsed with @babel/parser.
  3. Every obj.method(block) call where method is one of the six names and has exactly one argument is rewritten in place to __kt$helper(obj, block). Only matched calls are touched — the rest of the file stays byte-for-byte identical (no reprinting, no reformatting churn).
  4. A single import { ... } from 'kotlified-ts/runtime' is injected for the used helpers (non-module scripts get tiny inline const definitions instead).
  5. Nothing is added to any prototype; the runtime helpers are plain exported functions that bundlers tree-shake per helper.

Semantics notes

  • One-argument rule: only calls with exactly one argument are rewritten. In particular the native Function.prototype.apply(thisArg, argsArray) (2 arguments) is never touched, while a 1-argument fn.apply(block) is treated as Kotlin apply. (Typing note: fn.apply(block) on a function receiver doesn't type-check — CallableFunction.apply types thisArg as the function's this type, so prefer .run()/.also() for functions, or .apply() on objects.)
  • Shadow heuristic: when a file declares a real member with one of the six names (class A { let(fn) {} }, interface method signatures, object literal methods, function-valued properties), the plugin warns about rewritten calls of that name — they would silently bypass the real member. The rewrite still happens; use computed access obj['let'](fn) to keep the real method, or pass shadowWarn: false to silence.
  • Computed access (obj['let'](...)) is left alone.
  • super.let(...) is left alone.
  • TypeScript positions (typeof x.let, type annotations) are never rewritten.
  • The injected helper identifiers (__kt$let, __kt$apply, ...) are reserved — don't declare your own bindings with those names in transformed files.
  • Files that fail to parse (exotic syntax) are skipped with a warning and left untouched.

Options

kotlify({
    include: /\/src\//, // extra include filter (default: all transformable files)
    exclude: /\.spec\.ts$/, // exclude filter
    runtimeId: "my-scope/runtime", // import specifier for the runtime (default: kotlified-ts/runtime)
    shadowWarn: false, // silence shadow warnings (default: true)
});

Using the runtime directly (no Vite)

import { letExt, applyExt, runExt } from "kotlified-ts/runtime";

const x = letExt({ n: 1 }, (v) => v.n + 1);

Development

pnpm test      # unit + e2e (vite build + tsc type-check against the global d.ts)
pnpm lint      # oxlint + typecheck
pnpm build     # emit dist/ (ESM + .d.ts)