Independent implementation of a DataWeave-compatible runtime for Deno, written from scratch in TypeScript.
- Deno installed on your system.
- Deno extension for VS Code (recommended).
If you use VS Code and have the Prettier extension installed, it may
conflict with Deno's formatter. To use Deno's formatter automatically on save,
add the following to your .vscode/settings.json:
"[typescript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
}DenoWeave includes a built-in interactive web playground that lets you write transformations, edit payloads, and see the results live in your browser.
You can try it online at denoweave.carlosxfelipe.deno.net, or run it locally:
deno task playgroundThis will start a local server at http://localhost:8787/. The playground
features syntax highlighting, automatic evaluation, and a collection of built-in
examples to help you get started.
No install needed — run directly from the source:
echo '[{"name":"alice","active":true},{"name":"bob","active":false}]' | \
deno run --allow-read src/cli/main.ts \
--expr 'payload filter ($.active) map ((u) -> { name: upper(u.name) })'Output:
[{ "name": "ALICE" }]Or use a .dwl script file:
deno run --allow-read src/cli/main.ts \
--script transform.dwl \
--input data.jsonOr use as a library in your Deno project:
import { evaluate } from './src/mod.ts';
const result = evaluate(
`payload.users map ((u) -> { name: upper(u.name), active: u.enabled })`,
{ payload: { users: [{ name: 'alice', enabled: true }] } },
);
// → [{ name: 'ALICE', active: true }]The project includes several practical use-cases in the examples/ folder. Run
them from the project root:
- JSON Transformation: Load and transform a static file.
deno run --allow-read examples/json-to-json/run.ts
- JSON to DataWeave Literal (
application/dw): Serialize data into native DataWeave notation, useful for generating static configurations.deno run --allow-read examples/json-to-dw/run.ts
- Deep Descendant Selector (
..): Traverse deeply nested JSON to find values.deno run --allow-read examples/descendant-selector/run.ts
- ISO 8601 Date Math: Explore how the native Temporal API handles leap years
and periods.
deno run --allow-read examples/date-math/run.ts
- Module System (import): Import functions and variables from other
.dwlfiles.deno run --allow-read examples/modules/run.ts
- CSV to XML (CLI): Transform CSV to XML directly via the CLI.
deno task cli --script examples/csv-to-xml/transform.dwl --input examples/csv-to-xml/input.csv --out xml
- YAML Kubernetes Pods: Parse a K8s Deployment YAML and extract environment
variables.
deno run --allow-read examples/yaml-k8s-pod/run.ts
- Error Handling: Safely process a batch with malformed records using
try().deno run --allow-read examples/error-handling/run.ts
- HTTP Server: Transform incoming JSON payloads in real-time.
deno run --allow-net --allow-read examples/http-server/server.ts
- Connectors / ETL Flow: Fetch and transform real data from an external API.
deno run --allow-net --allow-read examples/connectors/flow.ts
- Docs-as-Code Pipeline: Orchestrate extraction, transformation, and
loading.
deno run --allow-net --allow-read examples/pipeline/run.ts
- NDJSON Logs Processing: Parse and filter streaming log lines.
deno run --allow-read examples/ndjson-to-json/run.ts <<< "n"
- Multipart Form Data: Extract files and fields from a raw HTTP upload body.
deno run --allow-read examples/multipart-to-json/run.ts <<< "n"
- URLEncoded Forms: Parse x-www-form-urlencoded into deeply nested JSON
structures.
deno run --allow-read examples/urlencoded-to-json/run.ts <<< "n"
- XLSX Aggregation: Read Microsoft Excel binary sheets and perform grouping
and aggregations.
deno run --allow-read examples/xlsx-to-json/run.ts <<< "n"
- Plain Text Splitting: Split, map and reduce unstructured text reports into
objects.
deno run --allow-read examples/text-processing/run.ts <<< "n"
- E-commerce Order Enrichment (CLI): Enrich an order payload using
vars,p()properties,expr?existence checks and custom date parsing — all features injected via CLI flags.deno task cli \ --input examples/ecommerce/input.json \ --script examples/ecommerce/transform.dwl \ --props examples/ecommerce/app.properties \ --var 'region=south-america' \ --var 'agentId=worker-07'
- Multi-output Report: Produce a JSON enriched list and a CSV export from
the same sales payload in a single
.dwlscript, using conditional fields and multi-output blocks.deno run --allow-read examples/multi-output/run.ts
Deno provides unique advantages for a lightweight data transformation engine that Node and Bun don't offer natively:
- Run from URLs (No Install): Users can execute DenoWeave directly from a
URL (e.g.,
deno run https://...) without needing apackage.json,node_modules, or an install step. - Single-Binary Compilation: The built-in
deno compilecross-compiles the entire engine into a standalone, zero-dependency executable natively for Linux, Mac, or Windows. - Strict Sandbox Security: Unlike Bun, Deno denies file, network, and
environment access by default. Executing an untrusted data transformation
script is guaranteed safe unless explicitly granted (e.g.,
--allow-read).
DenoWeave implements a fully-featured parser and evaluator that supports modern DataWeave 2.x syntax:
- Core Types: Strings, Numbers, Booleans, Null, Arrays, Objects.
- Dates & Math: Native Temporal API support for ISO 8601 Date and Period
literals (
|2024-02-20|,|P1D|) with full date/time math capabilities (e.g.|2024-02-28| + |P1D|). Also supports custom date parsing and formatting viaas Date {format: "dd/MM/yyyy"}. - Modules & Namespaces: Full support for
importstatements (e.g.import * from dw::core::Strings,import sum from custom::Math) and encapsulated namespace resolution. - Operations: Arithmetic, logical with short-circuit evaluation (
and,or,not), comparisons, default (default), casting (as), array/string/object concatenation (++), and range slicing (to). - Object Properties: Supports dynamic keys
(expr): value, shorthand{ name }, and conditional properties(key: value) if (cond). - Selectors: Deep descendant selector (
..) to traverse and query deeply nested data structures, field existence operator (expr?) to check if a key exists, optional chaining (?.) for safe navigation, and non-null assertion (!) to enforce values. - Context & Properties: Injection of context variables (
vars.region) and properties (p("store.id")) via the CLI. - Functions & Lambdas: Named functions (
fun), single-param lambdas ((x) -> x), multi-param lambdas, anonymous lambdas ($,$$). - Infix Higher-Order Functions:
map,filter,reduce, plus DataWeave-style infix usage ofgroupBy,orderBy,distinctBy,flatMap,mapObject,filterObjectandpluck. - Variables & Types: Local variables (
var), type hints (type). - Pattern Matching:
match/caseexpressions including literal match, type check (case is Type), and named capture with guards (case q if q > 100). - Scoping: Local scope evaluation via
do { ... }blocks. - Control Flow & Error Handling:
if / elseexpressions and safe evaluation usingtry(). - Multi-output Scripts: A single
.dwlfile can declare multipleoutput <mime> --- <body>blocks, each with its own output format. The CLI evaluates all blocks against the same input and prints each result separated by a blank line. - Smart Error Suggestions: When an undefined variable is referenced, the
runtime computes the Levenshtein distance against all names visible in scope
(locals,
vardeclarations, and the full stdlib) and suggests the closest match — e.g.Did you mean: "payload"?. - Utilities: Built-in stdlib functions including advanced deep object
merging via
update.
You can compile DenoWeave into a single, self-contained executable binary that runs on target systems without Deno or Node.js installed.
deno task compileThis generates the executable binary at build/denoweave (or
build/denoweave.exe on Windows).
You can build for other target operating systems using the --target flag:
# Target Linux (x64)
deno compile --allow-read --target x86_64-unknown-linux-gnu --output build/denoweave-linux src/cli/main.ts
# Target Windows (x64)
deno compile --allow-read --target x86_64-pc-windows-msvc --output build/denoweave-win src/cli/main.ts
# Target macOS (Apple Silicon / M1/M2/M3)
deno compile --allow-read --target aarch64-apple-darwin --output build/denoweave-mac src/cli/main.ts./build/denoweave --script examples/json-to-json/transform.dwl --input examples/json-to-json/input.json# Filter active users from a JSON file
deno task cli --input data.json \
--expr 'payload.users filter ((u) -> u.active)'
# Transform CSV → JSON with upper() and if
printf 'name,score\nAlice,95\nBob,72' | deno task cli \
--in csv --out json \
--expr 'payload map ((r) -> { name: upper(r.name), pass: if (r.score >= 80) true else false })'
# CSV → XML
deno task cli --input data.csv --out xml \
--expr 'groupBy(payload, (r) -> r.category)'
# Run DSL script from a .dwl file
deno task cli --script script.dwl --input data.json
# Multi-output script (two formats from one file)
deno task cli --script multi.dwl --input data.json%dw 2.0
output application/json
fun stockStatus(qty: Number) = qty match {
case 0 -> "OUT_OF_STOCK"
case q if q > 100 -> "BULK"
case q if q > 0 -> "AVAILABLE"
else -> "UNKNOWN"
}
---
payload.items map (item, index) -> do {
var total = item.qty * item.price * 1.10
---
{
position: index + 1,
product: item.name,
status: stockStatus(item.qty),
totalWithTax: round(total * 100) / 100
}
}
Output:
[
{
"position": 1,
"product": "Notebook",
"status": "AVAILABLE",
"totalWithTax": 1099.99
},
{
"position": 2,
"product": "Mouse",
"status": "AVAILABLE",
"totalWithTax": 65.98
},
{
"position": 3,
"product": "Keyboard",
"status": "OUT_OF_STOCK",
"totalWithTax": 0
}
]The .. operator allows you to recursively extract all values matching a
specific key from anywhere within a deeply nested JSON structure.
%dw 2.0
output application/json
var payload = {
company: {
departments: [
{
employees: [
{ name: "Alice", role: "Engineer" },
{ name: "Bob", role: "Manager" }
]
},
{
employees: [
{ name: "Charlie", contacts: [{ name: "Dave" }] }
]
}
]
}
}
---
{
all_names: payload..name,
roles: (payload..role) distinctBy $
}
Output:
{
"all_names": [
"Alice",
"Bob",
"Charlie",
"Dave"
],
"roles": [
"Engineer",
"Manager"
]
}A single .dwl file can produce multiple outputs in different formats. Declare
several output <mime> --- blocks, each with its own body expression. All
blocks share the same header declarations (%dw, var, fun, type).
%dw 2.0
var items = payload.items
output application/json
---
{ total: sizeOf(items), ids: items map ($.id) }
output application/csv
---
items map ((item) -> { name: item.name, price: item.price })
deno task cli --script report.dwl --input data.json
# → {"total":3,"ids":[1,2,3]}
#
# name,price
# Widget,9.99
# Gadget,24.99
# Doohickey,4.49You can map specific output blocks directly to files using the --target flag
(format: <block_index>:<file_path>).
deno task cli --allow-write --script report.dwl --input data.json \
--target 1:report.json \
--target 2:report.csvUnmapped blocks are printed directly to the console (stdout).
When an undefined variable is referenced, DenoWeave searches all names visible
in the current scope (local vars, function params, and the full stdlib) and
suggests the closest match using the Levenshtein algorithm:
# Script: payload.users map ((u) -> { name: u.nme })
# ^^^
# DenoWeave output:
# RuntimeError: Cannot access property "nme" of null
# Script: paylaod.users map ...
# DenoWeave output:
# ReferenceError: Undefined variable: "paylaod". Did you mean: "payload"?
Suggestions only appear when the edit distance is ≤ 3 to avoid noisy false positives.
| Category | Functions |
|---|---|
| String | upper, lower, trim, split, join, replace, contains, startsWith, endsWith, padLeft, padRight |
| Array | map, filter, reduce, groupBy, orderBy, distinctBy, pluck, first, last, sum, avg, min, max, zip, flatten |
| Object | keys, values, entries, merge, deepMerge, mapObject, filterObject, pick, omit, has |
| Math | abs, ceil, floor, round, sqrt, pow, log, mod |
| Type | typeOf, isNull, isEmpty, length |
DenoWeave supports the same data formats as the official DataWeave playground. All adapters are implemented in pure TypeScript/Deno with zero npm dependencies.
| Format | MIME type | Input | Output | Notes |
|---|---|---|---|---|
| JSON | application/json |
✅ | ✅ | Default format |
| CSV | application/csv |
✅ | ✅ | RFC 4180; configurable delimiter & quoting |
| XML | application/xml |
✅ | ✅ | Hand-written recursive-descent parser |
| YAML | application/yaml |
✅ | ✅ | Via @std/yaml (MIT) |
| NDJSON | application/ndjson |
✅ | ✅ | One JSON value per line |
| TEXT | text/plain |
✅ | ✅ | Raw string pass-through |
| URLENCODED | application/x-www-form-urlencoded |
✅ | ✅ | Via native URLSearchParams |
| MULTIPART | multipart/form-data |
✅ | ✅ | RFC 2046; auto-detects boundary |
| DW | application/dw |
✅ | ✅ | DataWeave literal notation (unquoted keys, |date| pipes) |
| XLSX | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
✅ | ✅ | Pure TS ZIP + ECMA-376; no native binaries |
The project includes a suite of more than 360 automated tests covering the lexer, parser, evaluator, stdlib, and all data adapters.
To run the tests:
deno task testTo run the tests in watch mode:
deno task test:watchTo run the linter:
deno task lintTo format the TypeScript codebase:
deno task fmtDenoWeave includes a built-in, custom pretty-printer specifically designed for
DataWeave (.dwl) scripts. It intelligently formats code while preserving all
comments, handles nested blocks, and correctly indents if/else and lambda
continuations.
To format all .dwl files in your project:
deno task fmt:dwlTo check for formatting issues without modifying files (useful in CI pipelines):
deno task fmt:dwl --checkDSL Code
↓ Lexer (src/lexer/)
Tokens
↓ Parser (src/parser/)
AST
↓ Evaluator (src/evaluator/)
Value
↓ Adapter (src/adapters/)
JSON / CSV / XML / YAML / NDJSON / TEXT / URLENCODED / MULTIPART / DW / XLSX
This is an independent, educational, and experimental project. It serves as an accessible bridge for developers who want to learn the DataWeave language and practice data transformation concepts, potentially preparing them for future work within the official MuleSoft ecosystem.
Because of this scope, its architecture is designed for simplicity and modern web environments:
- In-Memory Processing: DenoWeave currently loads the entire payload into memory to build its Abstract Syntax Tree (AST) and evaluate the transformation. This means it works exceptionally well for small to medium payloads but will hit V8 memory limits if you attempt to process extremely large datasets.
- Startup Time & Edge Computing: Built on the V8 JavaScript engine, DenoWeave natively leverages V8 Isolates. This enables near-instant startup times without compilation steps, making it an excellent fit for modern Edge environments (like Deno Deploy or Cloudflare Workers) where scripts need to start and execute in milliseconds.
- Future Evolution (Streaming & Wasm): To handle larger files in the future, the modern Deno ecosystem provides excellent native paths. The data adapters could be refactored to use the Web Streams API to process data chunk-by-chunk. Alternatively, the core evaluation engine could be ported to Rust and compiled to WebAssembly (Wasm) for near-native speeds.
Note that using the official MuleSoft DataWeave extension in VS Code may cause some noise, such as false-positive linting errors and engine incompatibilities. Because of this, I created my own lightweight syntax highlighting extension for DenoWeave:
DenoWeave is an independent, open-source project implemented from scratch in TypeScript/Deno with the sole purpose of interoperability and data compatibility. This project does not include or redistribute any proprietary code. Compatibility with many DataWeave features is a project goal, but full compatibility is not guaranteed.
MuleSoft, Anypoint Platform, and DataWeave are registered trademarks of MuleSoft, LLC, a subsidiary of Salesforce, Inc. This project has no affiliation, sponsorship, association, or endorsement of any kind with MuleSoft or Salesforce.
MIT