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

vitepress-plugin-idn

v0.1.3

Published

Indonesian language capabilities for VitePress: morphology-aware search, stemming, syllabification and hyphenation

Downloads

545

Readme

vitepress-plugin-idn

Indonesian-first search, stemming, syllabification and hyphenation for VitePress. One registration line, zero required configuration.

  • Setup-free Indonesian search in the default-theme nav bar (Ctrl/Cmd+K, /)
  • Offline, static-served index — works with no internet and no server (MiniSearch core)
  • stem(), syllabify(), hyphenateText() exportable for author tools
  • Optional client-side hyphenation of rendered content (soft hyphens)
  • English as a secondary language (language: 'en')

Install

npm i vitepress-plugin-idn

Quickstart (register the plugin)

// .vitepress/config.ts
import { defineConfig } from 'vitepress'
import { idnPlugin } from 'vitepress-plugin-idn/node'

export default defineConfig({
  vite: {
    plugins: [idnPlugin()] // ← the only required line; every option is optional
  },
})

The plugin is available from vitepress-plugin-idn/node; the root entry (vitepress-plugin-idn) is the client-safe utility entry and intentionally does not pull in the Node/Vite plugin.

That is the complete setup for Indonesian search:

  1. npm run docs:dev — the nav bar shows the search button; Ctrl/Cmd+K or / opens it.
  2. npm run docs:build — the search index and UI ship inside the static bundle.

Options

All options are optional; defaults give working Indonesian search.

| Option | Default | Meaning | | --- | --- | --- | | enabled | true | Master switch; false → no index, no UI | | language | 'id' | Site language: 'id' or 'en' | | include | ['**/*.md'] | Page globs (relative to srcDir) to index; exclude is applied after | | exclude | [] | Page globs to drop; frontmatter search: false always wins | | hyphenate.enabled | false | Insert soft hyphens into rendered content | | hyphenate.selector | '.vp-doc p, .vp-doc li, .vp-doc td' | Text containers | | hyphenate.minWordLength | 6 | Words shorter than this are untouched | | ui | 'nav' | 'nav' (default theme), 'external' (host mounts <IdnSearch />), false (index only) | | translations | — | Override any UI string | | minIndexSizeWarningMB | 5 | Build warning threshold for the serialized index |

Failure behavior is always visible (never silent): unknown languages and invalid setups fail/warn at build time, and runtime index mismatches show an error state in the UI. See specs/001-indonesian-search-plugin/ for the full option contract.

Custom themes: ui: 'external'

Third-party themes (or a custom Layout) can keep their own nav area and embed the search dialog themselves:

// .vitepress/config.ts
import { idnPlugin } from 'vitepress-plugin-idn/node'

export default defineConfig({
  vite: { plugins: [idnPlugin({ ui: 'external' })] },
})
<!-- any theme component -->
<script setup lang="ts">
import { IdnSearch } from 'vitepress-plugin-idn/vue'
import 'vitepress-plugin-idn/vue.css'
</script>

<template>
  <IdnSearch />
</template>

With ui: 'external' the plugin does not replace the theme's search button; it only serves the index, options and the IdnSearch component.

The vitepress-plugin-idn/vue.css import is required: the precompiled /vue entry carries no styling of its own, so the dialog renders unstyled without it. (The default ui: 'nav' path compiles the SFC through your Vue pipeline and does not need this import.)

Author utilities (public API)

import {
  stem,          // 'berlari' -> 'lari'   (id, Nazief-Adriani / en, Snowball)
  syllabify,     // 'pemerintahan' -> 'pe-mer-in-ta-han'
  hyphenateText, // inserts U+00AD soft hyphens at break points
  tokenize,      // lowercase, diacritic-folded, stop-word-free terms
  isStopWord,
  getStopWords,
  IDN_VERSION,
} from 'vitepress-plugin-idn'
  • stem(word, 'id' | 'en') and syllabify(word, 'id' | 'en') never throw and return their input unchanged when they cannot normalize it.
  • hyphenateText(text, { language, minWordLength }) never breaks URLs, digits or boundaries, is idempotent, and never throws.

Upstream capabilities (FR-023)

| Component | Role | Status & reason | | --- | --- | --- | | snowball-js | Snowball stemmer family | Adopted from the fork — consumed as @mlengse/snowball-js@^1.0.2 (MPL-1.1, published from the fork) for the en stem path (Snowball English / Porter2). Snowball's Indonesian rules are weaker than Nazief-Adriani for confix stripping, which is why id still uses the sastrawijs fork. This re-adopts the engine the interim implementation had swapped for the official stemmer (Porter). | | sastrawijs-ts | Indonesian stemming (Nazief-Adriani) | Adopted from the fork — consumed as sastrawijs-ts@^1.0.1 (MIT), a TypeScript port published from mlengse/sastrawijs-ts (the repo was renamed from sastrawijs, which GitHub redirects). Output is byte-identical to the original [email protected] across the full 67-word golden list, so the stem() contract and search index terms are unchanged. Root-word provenance derives from kateglo.com (CC BY-NC-SA 3.0); this is flagged in NOTICE. The stem() contract is engine-agnostic, so the dictionary can be swapped behind the unchanged API if a non-commercial term is a blocker. | | hyphenasi | Hyphenation patterns (id, en) | Adopted from the fork — consumed as hyphenasi@^1.15.0 (ISC), published from mlengse/hyphenasi (renamed from hyphen, which GitHub redirects). Its KBBI-derived Indonesian patterns are more accurate than upstream: scored against the 110-word PUEBI golden list it produces 11 invalid and 9 missed break boundaries versus upstream's 18 and 44, and it repairs real defects (jal-an→ja-lan, eko-no-mi→e-ko-no-mi). Also drives the English syllable engine. | | stopwords-iso | Stop-word lists | Adopted (MIT) — the id/en lists are vendored by scripts/update-stopwords.ts, regenerable, and attributed in NOTICE. | | kbbi-harvester-cdn | KBBI reference data (ISC) | Development-time only, never bundled. The validation toolkit under tools/kbbi/ compares stem() and syllabify() against the pinned data-v4 tag of this dataset: 33.268 derived words with their roots and part-of-speech classes, plus 73.768 hyphenation entries. It is fetched once into the gitignored .kbbi/ directory by node scripts/kbbi-validate.mjs snapshot, verified against the blob SHAs recorded in specs/002-validasi-kbbi-berbertahap/research.md, and never enters dist/, the npm package, or the runtime. Only curated word/root pairs are committed, as regression cases. See NOTICE for the license and specs/002-validasi-kbbi-berbertahap/quickstart.md for how to reproduce a measurement. | | kbbi-mcp-server | KBBI query tools | Optional, sampling only. Used by kbbi-validate sample to cross-check a sample of the bulk measurement and to gather word context. The bulk path never calls it, so the absence of an MCP server cannot move an accuracy number. | | vitepress-plugin-pagefind | External indexer | Dropped. Pagefind requires a separate binary and non-static asset pipeline; the spec needs a pure-build, static, same-origin index, which we build in-process instead. | | lunr.js / lunr-languages | Client search engine | Dropped. Replaced by minisearch (MIT), which offers pluggable processing (our stemming and stop-word pipeline) and a compact serialized index. |

License & notices

MIT. License text and third-party attributions ship with the package (LICENSE, NOTICE).