Spintax для JavaScript и TypeScript
Движок spintax теперь доступен отдельным npm-пакетом: @spintax/core. Ноль зависимостей, лицензия MIT, полная независимость от фреймворка — те же примитивы parse / render / validate, что и в WordPress-плагине, но применимые везде, где работает JavaScript.
Что это
@spintax/core — open-source, framework-agnostic движок spintax для JavaScript и TypeScript. Это компаньон WordPress-плагина Spintax — независимая TypeScript-реализация того же синтаксиса, с нулевой зависимостью от WordPress. Один движок, много поверхностей.
- Ноль зависимостей в рантайме. Работает без изменений на Cloudflare Workers, Node 18+ и в браузере.
- ESM-first, плюс CJS. Поставляет
.d.ts-типы для обеих модульных систем. - Лицензия MIT. Плагин остаётся под GPL; MIT/Expat совместим с GPL, так что оба уживаются чисто.
- Паритет проверяется с PHP-плагином через общий golden corpus — не обещание, а машинно-проверяемый гейт.
Установка
npm install @spintax/core
Быстрый старт
import { render, validate, extract } from '@spintax/core';
render('{Привет|Здравствуйте|Хай} %name%!', { context: { name: 'Ада' }, seed: 42 });
// → "Привет Ада!" (детерминированно для данного seed; пост-обработка по умолчанию)
validate('{a|b'); // → [{ severity: 'error', code: 'bracket.unclosed', … }]
extract('%title% {?promo?Скидка}'); // → { refs: ['title', 'promo'], sets: [], includes: [] }
С seed вызов render полностью воспроизводим. Без него каждый вызов возвращает свежий случайный вариант из пространства шаблона.
API
Маленькое и острое ядро. Каждая функция принимает строку или уже разобранный Ast (из parse) — можно распарсить один раз и переиспользовать.
| Функция | Что делает |
|---|---|
parse(src) | Разобрать один раз в непрозрачный версионированный Ast для переиспользования — это handle для производительности, не формат сериализации. |
render(input, opts?) | Рендер в одну строку. Снисходительный — никогда не бросает исключение на битой разметке. Косметическая пост-обработка (пробелы, регистр, экранирование URL/email) включена по умолчанию. |
validate(input, opts?) | Вернуть диагностику. Валиден ⇔ нет severity:'error'. Неразрешённая %var% — предупреждение, не ошибка. Вердикты plural зависят от локали. |
extract(input) | Список ссылок на переменные refs, имён #set и целей #include — основа двухфазного prefetch для асинхронных include. |
analyze(input, opts?) | extract + validate + best-effort перепись конструкций. Слой статистики для инструментов. |
neutralize(value) | Экранировать недоверенный текст из данных, чтобы его нельзя было принять за разметку spintax. Text-safe, не HTML-экранирование. |
render принимает context (карту переменных), seed, locale (формы plural, например ru для 3-формного правила), опциональный includeResolver от хоста, переключатель postProcess и ограничитель maxDepth для вложенности и #include.
Тот же синтаксис, что вы уже знаете
Это полная поверхность spintax — справочник по синтаксису применим дословно.
| Конструкция | Пример | Смысл |
|---|---|---|
| Перечисление | {a|b|c} | выбрать одно (вложенность: {a|{b|c}}) |
| Permutation | [a|b|c] | выбрать N, перемешать, склеить — настраиваемые сепараторы |
| Переменная | %var% | подставить значение из контекста |
| Локальный set | #set %v% = value | объявить переменную (одна строка) |
| Условие | {?VAR?then|else} | ветвление по значению (гайд) |
| Plural | {plural %n%: одна|пара|много} | согласование с числом по локали (гайд) |
| Include | #include "slug-or-id" | встроить другой шаблон (разрешает хост) |
| Комментарий | /# … #/ | вырезается до рендера |
Паритет с WordPress-плагином
«Независимая реализация, но паритет там, где он важен» — это весь смысл пакета, и он обеспечивается, а не декларируется. Общий golden corpus из языконезависимых фикстур (template, context, locale, seed) → expected используется обоими — тест-сьютом PHP-плагина и этим TypeScript-сьютом.
- Детерминированное поведение под паритетом: вердикты валидации, формы plural, truthiness условий, схлопывание
#setи пайплайн пост-обработки дают идентичный вывод в обоих движках. - Выбор RNG — нет. Seeded-рендеры воспроизводимы внутри движка; кросс-движковый паритет случайных последовательностей — сознательный non-goal. Валидный вариант вы получите в любом случае, просто не тот же самый.
Это не построчный порт GPL-кода на PHP — он переписан из поведенческого контракта плюс этого корпуса, что и позволяет держать его чисто под MIT.
Один движок, много поверхностей
Пакет — это общий рантайм всей экосистемы; каждая поверхность ниже потребляет тот же публичный API и никогда не является приватным форком:
- Песочница на этом сайте (/play/) рендерит на клиенте, прямо в браузере.
- Референсный Cloudflare Worker — валидация и preview-рендер по HTTP.
- Telegram-бот @spintaxnetbot — вставьте шаблон и получите валидацию плюс несколько готовых вариантов прямо в чате.
Поскольку примеры импортируют @spintax/core и ничего больше, каждый из них доказывает, что API удобен, не засоряя его. Работает для Worker и бота — заработает и в вашем проекте.