jslt-js

中文文档
0.4.0发布于 2026/11/04Apache-2.0ESM + CJS零依赖

JavaScript port of Schibsted JSLT — JSON query and transformation language (https://github.com/schibsted/jslt)

原始使用文档
文档目录20 节
  1. For Java developers
  2. Quick start
  3. Install
  4. API
  5. compile(source, sourceName, options?)
  6. expr.applyInput(inputNode)
  7. expr.applyVariables(vars, inputNode)
  8. fromJS(value) / toJS(node)
  9. readTree(jsonString)
  10. Imports
  11. Extension functions
  12. CLI
  13. Build
  14. Tests
  15. Parity notes
  16. Ecosystem
  17. Implementations
  18. Tooling and IDE plugins
  19. Used with the Java library
  20. Learn more

jslt-js

JavaScript port of JSLT — the JSON query and transformation language. Faithful translation of the Java engine: same grammar, same builtins, same semantics. Isomorphic (browser + Node.js), zero runtime dependencies.

Try it in the playground — live JSLT editor with syntax highlighting, error markers and shareable links.

For Java developers

If you know the Java JSLT library this maps directly:

JavaJS
Parser.compileString(source)compile(source, name)
expression.apply(input)expr.applyInput(fromJS(input))
expression.apply(variables, input)expr.applyVariables(vars, fromJS(input))
new ObjectMapper().readTree(json)readTree(json)
Jackson JsonNode treeJsonNode tree (same API shape)
toJson()toJS()

Quick start

import { compile, fromJS, toJS } from "jslt-js";

const expr = compile(`.name + " wins"`, "example.jslt");
const result = toJS(expr.applyInput(fromJS({ name: "Alice" })));
// → "Alice wins"

Install

npm install jslt-js          # ESM (Node 18+, browsers)

Browser (classic script / file:// / no bundler): use the prebuilt IIFE — it sets globalThis.JSLT (same shape as the ESM API, including extensions):

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jslt-bundle.js"></script>
<script>
  const expr = JSLT.compile('{"hi": .name}');
  console.log(JSLT.toJS(expr.applyInput(JSLT.fromJS({ name: "Ada" }))));
</script>

Do not rely on https://esm.sh/jslt-js@…?bundle for browsers — older publishes pulled node:crypto into the graph. Prefer dist/jslt-bundle.js (or ESM from [email protected]+).

CJS consumers (require()):

const { compile, fromJS, toJS } = require("jslt-js");

The CJS build is at dist/jslt.cjs and is selected automatically via the exports field.

API

compile(source, sourceName, options?)

Parses and compiles a JSLT template. Returns an ExpressionImpl.

const expr = compile(source, "my-transform.jslt", {
  resolver: { resolve: (name) => fs.readFileSync(name, "utf8") },
  functions: myCustomFunctions,   // array of Function objects
});

Options:

OptionTypeDescription
resolver{ resolve(name): string }Loads imported modules by name
functionsFunction[]Custom/extension functions to register
objectFilterJsonFilter or stringWhich object values are kept in output objects. Default drops null, {} and []. A string is a JSLT expression returning true for values to keep, as in Java's Parser.withObjectFilter(String)

Object key filter

By default, object keys whose value is null, {} or [] are omitted from the output. To change that, pass objectFilter:

import { compile, TrueJsonFilter } from "jslt-js";

compile(source, name, { objectFilter: new TrueJsonFilter() });   // keep every key
compile(source, name, { objectFilter: "true" });                 // same, as a JSLT expression
compile(source, name, { objectFilter: ". != null" });            // drop only nulls

DefaultJsonFilter, TrueJsonFilter, JsltJsonFilter (wraps a compiled expression) and the JsonFilter base class are exported.

expr.applyInput(inputNode)

Apply the compiled expression to a JsonNode input.

expr.applyVariables(vars, inputNode)

Apply with external variables. vars is a plain JS object mapping variable names to JsonNode values:

expr.applyVariables({ foo: fromJS(42) }, fromJS(null));

fromJS(value) / toJS(node)

Convert between plain JS values and the internal JsonNode tree. toJS is safe for JSON.stringify large integers are downgraded from BigInt when they fit in a JS number.

readTree(jsonString)

Parse a JSON string to a JsonNode. Equivalent to Jackson's ObjectMapper.readTree for most purposes. Note: uses JSON.parse internally, so whole-valued floats ("42.0") come back as IntNode(42) — the same type-erasure limitation as any JS JSON parser. JSLT expressions and templates are not affected since the engine uses its own type-aware lexer for literals.

Imports

JSLT import statements are resolved via the resolver option:

// File-system resolver (Node only)
import { readFileSync } from "fs";
import { resolve, dirname } from "path";

const jsltDir = dirname(jsltFilePath);
const opts = {
  resolver: { resolve: (name) => readFileSync(resolve(jsltDir, name), "utf8") },
};
const expr = compile(source, jsltFilePath, opts);

Extension functions

Register custom functions the same way as Java's Parser.withFunctions:

const myFn = {
  getName: () => "greet",
  getMinArguments: () => 1,
  getMaxArguments: () => 1,
  call: (_input, [nameNode]) => fromJS("Hello " + nameNode.asText()),
};

const expr = compile(`. | greet(.name)`, "t.jslt", { functions: [myFn] });

An opt-in extension pack ships with the library. None of these are stock JSLT functions, and none reuses a built-in name (stock flatten() recurses and takes one argument, stock sum() throws on a non-number, so the pack's versions are flattened() and summed()):

FunctionResult
flattened(a, b, ...)the array arguments concatenated, one level deep; non-arrays skipped
summed(array)sum of the numeric elements; null if not an array or none are numbers
enumerate(array)[{i, v}, ...]
range(n)[0, 1, ..., n-1]; empty for a missing, non-number or non-positive n
money(x)x rounded half-up to 2 decimals; null for a non-number
fullName(first, last)"LAST/FIRST" upper-cased from the trimmed strings; null if both are empty
base64Decode(s)UTF-8 text from Base64; null for a blank or invalid input
distinct(array)the elements without repeats (JSON equality), first occurrence kept; [] for a non-array
minOf(array) / maxOf(array)smallest / largest element, nulls ignored; all numbers or all strings, else null (stock min()/max() take exactly two arguments)

They mirror the custom functions of a Java host and are checked against the same test/resources/function-parity.json.

import { extensions } from "jslt-js/extensions";

const expr = compile(source, name, { functions: extensions });

CLI

npx jslt [--extensions] <transform.jslt> [input.json|-]

Reads JSON from input.json or stdin. --extensions loads the built-in extension pack. Imports resolve relative to <transform.jslt>'s directory.

echo '{"name":"Alice"}' | jslt greet.jslt -
cat booking.json | jslt --extensions ext.jslt -

Build

npm run build     # → dist/jslt-bundle.js (browser global) + dist/jslt.cjs (Node CJS)

The browser bundle exposes globalThis.JSLT with the full API including extensions.

Tests

npm test

Runs unit tests plus the upstream conformance fixtures (query-tests.json, function-tests.json, function-declaration-tests.yaml, etc.), vendored from schibsted/jslt — see test/resources/SOURCE.md for the exact upstream commit.

The conformance suite is the primary correctness contract for this port, not an incidental test. Every file under src/ is a line-for-line // Port of X.java translation; "does it pass the upstream fixtures unchanged" is what defines parity with the Java engine, and that's re-verifiable against future upstream releases by re-syncing test/resources/.

All upstream conformance fixtures pass with 0 skips. The YAML fixtures require js-yaml (devDependency).

Parity notes

  • Numeric types: matches Jackson exactly — IntNode/LongNode/BigIntegerNode/DoubleNode with cross-type equals (Jackson 2.6+ semantics).
  • parse-time/format-time: implemented via Intl.DateTimeFormat. Supports Java SimpleDateFormat tokens (yyyy MM dd HH mm ss S/SS/SSS z Z X). Returns/accepts seconds since Unix epoch (double), same as Java.
  • Experimental module: import "http://jslt.schibsted.com/2018/experimental" works out of the box — provides group-by.
  • FunctionWrapper: not ported — in Java it wraps a static Method via reflection. In JS, any object with {getName, getMinArguments, getMaxArguments, call} is already a JSLT function, no wrapper needed.
  • parse-url: implemented via WHATWG URL. Omits path when the original URL has no explicit path (matches java.net.URL.getPath() returning "").
  • Deferred from the Java API: ClasspathResourceResolver, FileSystemResourceResolver (use the resolver option instead), and the REPL/playground.

Ecosystem

JSLT has a small but varied ecosystem, made by several authors, including this port's. Statuses are as of October 2026, so see each project for its current state.

Implementations

ProjectLanguageNotes
schibsted/jsltJavaThe reference implementation and language definition. This port tracks its conformance fixtures.
jslt-js (this repo)JavaScriptPure JavaScript for browsers and Node, no native code; passes the upstream conformance suite.
jslt2JavaA VM-based implementation, mentioned in the upstream README.
jslt-nodeRust, Node bindingsA native N-API addon over a Rust implementation; its README calls it a proof of concept.

Tooling and IDE plugins

  • JSLT demo (source): the upstream project's playground, which runs the Java engine on a server
  • jslt-editor: an embeddable CodeMirror 6 editor with highlighting, completion, hover docs, formatting and error markers.
  • jslt-intellij-plugin: IntelliJ IDEs; highlighting, navigation, rename, validations and an execution dialog.
  • jslt-lang: syntax highlighting for Visual Studio Code, on the Visual Studio Marketplace.
  • jslt-language-server: LSP server (diagnostics, completion, hover, formatting) for any editor that uses LSP.
  • jslt-playground (open it): Pure JS implementation, runs in your browser, with tabs for imports, JSON or XML input and output, and shareable links.
  • jslt.tmbundle: TextMate; highlighting, symbol list, snippets, "Check Syntax" and "Apply to JSON" actions.
  • Pygments: ships a JSLT lexer for .jslt files, for Python and documentation tools.
  • vscode-jslt: Visual Studio Code and forks (Cursor, VSCodium); highlighting, live error checking, completion, hover docs, formatting, snippets and "JSLT: Apply to JSON File…".

Used with the Java library

Listed in the upstream README: the Apache Camel JSLT component, Apache NiFi as a processor, Kafka Connect transforms at Willhaben.at, IBM Cloud Pak for Business Automation and the Pincette event sourcing framework.

Learn more

A talk on the language design (slides) and a paper describing, among other things, how Schibsted uses JSLT.

版本与发布记录

jslt-js 共发布 13 个版本,首次发布于 2026/07/03;过去 12 个月发布了 13 个版本。

版本发布日期距今
0.4.0latest2026/11/041 天前
0.3.12026/11/041 天前
0.3.02026/10/0728 天前
0.2.02026/10/0728 天前
0.1.82026/07/163 个月前
0.1.72026/07/034 个月前

关于 jslt-js 的常见问题

jslt-js 是什么?

JavaScript port of Schibsted JSLT — JSON query and transformation language (https://github.com/schibsted/jslt)

如何安装 jslt-js?

在项目目录运行 npm install jslt-js,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 jslt,可以用 npx jslt 直接运行。

jslt-js 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/jslt-js 是否存在。

jslt-js 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

jslt-js 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

jslt-js 可以商用吗?

jslt-js 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

jslt-js 还在维护吗?

最新版本 0.4.0 发布于 2026/11/04(1 天前);至今共发布 13 个版本,其中过去 12 个月发布了 13 个;首次发布于 2026/07/03。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。