Wakaru — JavaScript decompiler and bundle unpacker
Wakaru recovers readable source from production JavaScript: it splits bundles into modules, reverses transpiler helpers (async/await, classes, JSX, spread, optional chaining, …), and undoes minification. It recovers structure deterministically and applies only conservative renaming heuristics — most mangled locals stay short unless a source map is provided.
When to use this
- A file is one giant line, or full of
_interopRequireDefault,__awaiter,e,t,rparameters,void 0,!0/!1. - You have a webpack bundle, supported Vercel ncc CommonJS output with an IIFE webpack bootstrap, or an esbuild/Bun/Metro/Browserify/Cocos Creator 2.x/ Closure ModuleManager/SystemJS/AMD/Rollup/Vite bundle and need the individual modules.
- You have a Bun single-file PE, Mach-O, or ELF executable and need its embedded JavaScript without running it.
- You need every file stored in a Bun single-file executable, including binary assets, WebAssembly, native add-ons, or embedded databases.
- You have compiled Vue 3 component JavaScript and want a best-effort
.vueartifact for inspection. - A stack trace points into vendored/minified code you can't read.
- You're auditing what a site or dependency actually ships.
Setup
Requires the CLI. Prefer running via npx (no global install):
npx wakaru --version
If invoked repeatedly, install once: npm install -g wakaru@latest.
In this document wakaru means npx wakaru unless installed globally.
Core workflows
1. Decompile a single file
For one minified/transpiled file. Use --json for structured stdout. Without
-o, the decompiled source is included in the JSON code field; with -o,
the file is still written and code is omitted:
echo '<code>' | wakaru --json
# → {"code":"...readable...","warnings":[],"elapsed_ms":N}
Or file-to-file: wakaru input.js -o output.js (stdout if -o omitted).
2. Unpack a bundle (the important one)
A bundle can explode into thousands of modules — do not dump them all into context. Unpack to a directory, inspect the JSON output, then open only the files you need:
wakaru bundle.js --unpack --json -o out/
# → {"detected_formats":["webpack4"],"modules":[{"filename":"module-0.js"},...],
# "total":42,"failed":0,"warnings":[],"elapsed_ms":N}
Use a fresh output directory. Wakaru refuses to write into a non-empty
directory unless --force is passed; use --force only after confirming that
overwriting its contents is acceptable.
Then read specific files from out/ (e.g. out/module-0.js) on demand. Triage
by size first (ls -lS out/) — the largest modules are usually vendored
libraries; the app code is often smaller and more numerous.
Variants:
wakaru dist/ --unpack --json -o out/ # scan a build-output directory
wakaru entry.js chunk.js --unpack -o out/ # explicit entry + chunk files
wakaru ./compiled-app --unpack --raw -o out/ # extract a Bun single-file executable safely
wakaru bun extract ./compiled-app -o raw/ # dump every embedded Bun file byte-for-byte
Bun single-file executable extraction requires an explicit executable path
(directory scans and stdin handle JavaScript only). --unpack selects the
JavaScript-like records and sends them through
bundle splitting. Prefer --raw when comparing their shipped representation.
Use wakaru bun extract instead when the task needs the container itself. It
writes every validated file record below raw/files/ without decoding or
transforming it and records loader metadata and byte ranges in
raw/manifest.json. Add --json for the same manifest on stdout.
--include-internals also emits opaque Bun source-map, JavaScriptCore bytecode,
and module-info regions; do not treat source-map.bunmap as a v3 JSON map.
The extractor supports Bun 1.3.3+.
Module filenames are module-<id>.js unless the bundle carries usable naming
hints (resource paths, dependency-map requests, original input paths), which
yield readable relative filenames; collisions are made unique. Raw output
keeps provisional extraction names.
When you need to check a recovered module against the input, add
--provenance to the unpack command. Look up its emitted filename in
out/provenance.json for the source input and [start, end) byte ranges.
3. Recover names / original source when a map exists
wakaru input.js --source-map input.js.map -o output.js # recover names
wakaru extract input.js.map -o src/ # dump sourcesContent
Input --source-map is single-file only and cannot be combined with
--unpack; extracted modules do not retain the bundle's generated coordinates.
Use --emit-source-map when unpacked output maps are needed.
4. Recover Vue 3 components as SFC artifacts
Use --vue-sfc when the input is compiled Vue 3 component JavaScript or a
bundle likely to contain it. Recovery is best-effort and additive: unpack mode
still writes JavaScript for every module, and recoverable Vue modules also get
sibling .vue artifacts.
wakaru input.js --vue-sfc -o output.js
wakaru input.js --vue-sfc -o App.vue
wakaru bundle.js --unpack --vue-sfc --json -o out/
For one file, use -o to write recovered Vue output; without it, stdout
remains JavaScript. For batch analysis, prefer --unpack --vue-sfc --json and
inspect the JSON output first. Each modules entry describes an output artifact. Its kind is
javascript or vue_sfc; Vue-related status values are
recovered_vue_sfc, vue_sfc_source_js, and vue_sfc_fallback_js. Open
recovered .vue files for template inspection, but keep the paired JavaScript
artifact around when recovery falls back or looks too heuristic. Do not
present recovered SFCs as original source.
Heavily obfuscated input
For string-array encoding, control-flow flattening, VM protectors, and similar obfuscation, first use webcrack to strip the obfuscation. Leave unpacking and unminifying to Wakaru:
# 1. Strip the obfuscation; leave unpacking and unminifying to Wakaru.
npx webcrack --no-unpack --no-unminify obfuscated.js > deobfuscated.js
# 2. Recover readable modules.
npx wakaru deobfuscated.js --unpack -o out/
Rewrite levels — pick by intent
--level minimal— near-zero semantic change. Prefer for security review, auditing, or diffing to minimize semantic risk, but do not treat its output as a formal equivalence guarantee.--level standard— default; balanced readability and correctness.--level aggressive— maximum readability; stronger heuristics that may alter edge-case behavior. Use when you just need to understand the code.--unpack=inspect— recursively retain finer scope-hoist boundaries for static inspection. The resulting module graph may not preserve runtime initialization order; the CLI prints a warning whenever this mode is used. Add--rawindependently to skip readability transforms.
By default, Wakaru removes only dead code introduced by its own transforms and
preserves dead code already present in the input. Use --dce when a full
reachability sweep is desired.
Interpreting output
- Exit code 0 = success, non-zero = failure (parse error, I/O). Errors go
to stderr;
--jsonoutput goes to stdout. - Inspect every JSON warning's
is_errorfield. Entries withis_error: falseare non-fatal; an error-class warning makes the command fail even though the JSON output and successfully recovered files may still be written. - Use
--diagnosticsto catch emitted declaration conflicts during decompile or unpack, includingvarversuslet/const. Aduplicate_declarationwarning is an error even iffailedis zero; legal repeatedvardeclarations are allowed. failedin unpack JSON counts modules that errored during decompilation;totalis the module count. Treatfailed > 0as a failed run.- With
--vue-sfc,recovered_vue_sfcmeans a.vueartifact was written;vue_sfc_source_jsis the paired JavaScript for that recovered module; andvue_sfc_fallback_jsmeans the module looked Vue-like but stayed JavaScript. - Mangled short names (
e,t,n) in the output are expected without a source map — Wakaru renames only where the code gives evidence. Pair with an LLM renamer or a source map if names matter.
Safety
Only decompile code you are authorized to analyze. Reading what a third party ships is a legitimate security-research and debugging activity; using it against targets without consent may be illegal. You are responsible for compliance.