# Spintax for JavaScript & TypeScript The spintax engine now ships as a standalone npm package: `@spintax/core`. Zero dependencies, MIT-licensed, and framework-agnostic — the same parse / render / validate primitives that power the WordPress plugin, now usable anywhere JavaScript runs. ## What it is `@spintax/core` is an open-source, framework-agnostic [spintax](https://spintax.net/docs/syntax.md) engine for JavaScript and TypeScript. It is a **companion** to the [Spintax WordPress plugin](https://wordpress.org/plugins/spintax/) — an _independent_ TypeScript implementation of the same syntax, with **zero WordPress dependency**. One engine, many surfaces. - **Zero runtime dependencies.** Runs unchanged on Cloudflare Workers, Node 18+, and in the browser. - **ESM-first, dual CJS.** Ships `.d.ts` types for both module systems. - **MIT licensed.** The WordPress plugin stays GPL; MIT/Expat is GPL-compatible, so the two coexist cleanly. - **Parity-verified** against the PHP plugin by a shared golden corpus — not a promise, a machine-checked gate. ## Install ```bash npm install @spintax/core ``` ## Quick start ```js import { render, validate, extract } from '@spintax/core'; render('{Hello|Hi|Hey} %name%!', { context: { name: 'Ada' }, seed: 42 }); // → "Hi Ada!" (deterministic for a given seed; post-processed by default) validate('{a|b'); // → [{ severity: 'error', code: 'bracket.unclosed', … }] extract('%title% {?promo?Sale}'); // → { refs: ['title', 'promo'], sets: [], defs: [], includes: [] } ``` With a `seed`, `render` is fully reproducible. Omit it and every call returns a fresh random pick from the template's space. ## The API A small, sharp core. Every function accepts a raw `string` _or_ a parsed `Ast` (from `parse`), so a consumer can parse once and reuse. | Function | What it does | | --- | --- | | `parse(src)` | Parse once into an opaque, versioned `Ast` for reuse — an in-memory perf handle, not a serialization format. | | `render(input, opts?)` | Render to a single string. **Lenient** — never throws on malformed markup. Cosmetic post-processing (spacing, capitalization, URL/email shielding) is on by default. | | `validate(input, opts?)` | Return diagnostics. Valid ⇔ no `severity:'error'`. An unresolved `%var%` is a warning, not an error. Locale-aware plural verdicts. | | `extract(input)` | List variable `refs`, `#set` and `#def` names, and `#include` targets — powers a two-phase prefetch for async includes. | | `analyze(input, opts?)` | `extract` + `validate` + a best-effort construct census. Stats layer for tooling. | | `neutralize(value)` | Shield untrusted, data-derived text so it can't be re-interpreted as spintax markup. Text-safe, not HTML escaping. | `render` takes `context` (the variable map), `seed`, `locale` (plural buckets, e.g. `ru` for the 3-form rule), an optional host-injected `includeResolver`, a `postProcess` toggle, and a `maxDepth` guard for nesting and `#include`. ## The same syntax you already know It is the full spintax surface — the [syntax reference](https://spintax.net/docs/syntax.md) applies verbatim. | Construct | Example | Meaning | | --- | --- | --- | | Enumeration | `{a|b|c}` | pick one (nestable: `{a|{b|c}}`) | | Permutation | `[a|b|c]` | pick N, shuffle, join — configurable separators | | Variable | `%var%` | substitute a context value | | Local set | `#set %v% = value` | define a variable (one line); a macro — re-rolled at every reference | | Local def | `#def %v% = value` | same shape, resolved once per render and held everywhere | | Conditional | `{?VAR?then|else}` | value-driven branch ([guide](https://spintax.net/docs/conditional-spintax.md)) | | Plural | `{plural %n%: one|few|many}` | grammatical agreement by locale ([guide](https://spintax.net/docs/plural-spintax.md)) | | Include | `#include "slug-or-id"` | embed another template (host-resolved) | | Comment | `/# … #/` | stripped before rendering | ## Parity with the WordPress plugin "Independent implementation, but parity where it counts" is the whole point of the package — and it is enforced, not intended. A **shared golden corpus** of language-neutral fixtures `(template, context, locale, seed) → expected` is consumed by **both** the PHP plugin's test suite and this TypeScript suite. - **Deterministic behavior is parity-gated:** validation verdicts, plural buckets, conditional truthiness, `#set`/`#def` resolution, and the post-process pipeline assert identical output in both engines. - **RNG selection is not.** Seeded renders are reproducible _within_ an engine; cross-engine random-sequence parity is a deliberate non-goal. You get a valid pick either way, just not the same one. It is not a line-by-line port of the GPL PHP — it is reimplemented from the behavior contract plus that corpus, which is exactly what keeps it cleanly MIT. ### Pairs naturally with LLMs Have a model draft a spintax template _once_, then generate unlimited variations on-device — deterministic, free, and offline, with no per-generation API calls. The LLM handles creativity; the engine handles scale. That is the whole premise of Spintax, now available as a two-line import in any JS project. Curious what the "no per-generation API calls" part is worth? See [what AI content actually costs](https://spintax.net/ai-content-costs.md). Coding agents get the condensed version: the [spintax-engines skill](https://spintax.net/.well-known/agent-skills/spintax-engines/SKILL.md) covers install and API for every engine, and templates can be validated over MCP at `https://spintax.net/mcp`. ## One engine, many surfaces The package is the shared runtime behind the whole ecosystem — every surface below is a _consumer_ of the same public API, never a private fork: - **The playground** on this site ([/play/](https://spintax.net/play/)) renders client-side in your browser. - **A Cloudflare Worker** reference API — validate and preview-render over HTTP. - **The [@spintaxnetbot](https://t.me/spintaxnetbot) Telegram bot** — paste a template, get validation plus a handful of rendered variations, right in chat. Because the examples import `@spintax/core` and nothing else, each one _proves_ the API is usable without polluting it. If it works for the Worker and the bot, it works for your project too. --- [View on npm](https://www.npmjs.com/package/@spintax/core) [Source on GitHub](https://github.com/investblog/spintax-js) [Open in playground](https://spintax.net/play/) [Back to all guides](https://spintax.net/docs.md) --- Source: https://spintax.net/spintax-for-javascript/