About wakaru
Wakaru is a CLI skill for developers inspecting production JavaScript. It decompiles minified or transpiled code and splits supported bundles into modules, with source-map and Bun executable extraction workflows.
Key features
- 01Decompile minified and transpiled JavaScript
- 02Unpack supported bundles into modules
- 03Extract files from Bun executables
- 04Recover names with source maps
- 05Reconstruct Vue 3 SFC artifacts
- 06Decode React Server Components payloads
Use cases
- 01Inspect code behind a minified stack trace
- 02Audit files shipped by a site or dependency
- 03Extract embedded JavaScript without running a Bun executable
- 04Review recovered Vue components or RSC payloads
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ pionxzh/ wakaru --skill wakaruAfter installing, type/wakaruin Claude Code to confirm it loaded.
Details
From the project READMEWakaru — 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, Turbopack (Next.js 15.3+) chunks, 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 have a Google Tag Manager container (
gtm.js) and need its Custom HTML and Custom JavaScript code as separate files:wakaru gtm.js --unpack -o out/writesmacros/,tags/, and anindex.mdthat says what each__gtm_macro_<n>is and when each tag fires. - 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 as the bundler emitted it
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. If stderr shows note: <input> embeds N original source file(s) in Bun source maps, run the wakaru bun extract command it names
and read sources/ before the decompiled output. Prefer --raw when
comparing their shipped representation:
it still splits statements and declarations so the chunks link as ESM, so use
wakaru bun extract when the exact bytes matter.
Use wakaru bun extract instead when the task needs the container itself. It
writes every validated file record below raw/files/ and records loader
metadata and byte ranges in raw/manifest.json. Files are stored bytes, except
records with "encoding": "utf16le" (Bun 1.4.1+ non-ASCII text), which are
written as UTF-8; their bytes and executable_range describe the stored
UTF-16, and output_bytes the written file.
If the app was compiled with --sourcemap, raw/sources/ holds the original
pre-bundle files (for example TypeScript with types and comments), and the
manifest's sources list maps each original path to its output_path.
Check it before decompiling: when it is present, read those files instead of
the bundled JavaScript. A .. path component is written as %2E%2E. A
record listed in skipped_sources had a map Wakaru could not read; its
sources are missing, so fall back to its decompiled JavaScript. 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 original sources are written without this flag.
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.
5. Decode a captured React Server Components payload (experimental)
A React Server Components payload (React's Flight wire format) is data, not
JavaScript: do not run it through decompilation or unpacking. Next.js embeds
it in page HTML as self.__next_f.push(...) calls.
wakaru rsc page.html -o rsc.json # saved Next.js page or script
wakaru rsc payload.rsc -o rsc.json # raw payload rows
The JSON holds rows (hex id, tag, kind, raw payload, and json when
React parses it), assets (the script and stylesheet paths the payload loads),
scripts (a page's <script src> files, which include the bundler runtime the
payload never names; download both lists and pass them to --unpack), and problems (rows and push calls
that did not decode; the rest is still written). format is raw or
next.__next_f. Input with no payload fails. Model rows keep React's $
references ("$L3") as strings: the tree they describe is not reconstructed.
The output can change in any release.
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 the readability pipeline;--levelhas no effect with--raw.
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 = a fatal error (bad arguments, I/O) or
at least one error-class warning. 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 exit non-zero, but Wakaru still writes the JSON output and every artifact; modules without an error-class warning remain usable. - Use
--diagnosticsto catch emitted declaration conflicts during decompile or unpack, includingvarversuslet/const, and output that fails to re-parse.duplicate_declarationandoutput_parse_*warnings are error-class; legal repeatedvardeclarations are allowed. Without--diagnostics, emitted modules are not re-parsed. - A
cross_module_class_callwarning (non-error) means that module may call a recovered class withoutnewat runtime. Rerun with--level minimalif the output must execute. - A
commonjs_export_unrecoveredwarning (non-error) means an ESM output module still accesses the listed exports throughexportsormodule.exports. Those accesses throw aReferenceErrorwhen they run, so treat that module's export surface as incomplete. - A module that keeps a
require()call (lazy, conditional, or after a side effect) stays CommonJS: ESM has norequire. Acommonjs_require_unrecoveredwarning (non-error) means an ESM output module still callsrequirefor the listed specifiers anyway; those calls throw when they run. - A
commonjs_require_of_esmwarning (non-error) means a CommonJS output module requires a module now recovered as ESM, sorequirereturns that module's namespace; a read of a replacedmodule.exportsvalue, or of a default whose interop was removed or whose provider has no default export, gets the namespace instead, and a write to it fails. - A
commonjs_exports_escapewarning (non-error) means a module passes a provider's exports object to a function in another module or one wakaru cannot follow, and that provider is now ESM. If the function writes to the object, it throws at runtime; check that call before running the output. - A
missing_imported_namewarning (non-error) means an ESM output module imports a name its sibling ESM module does not export. Node cannot link it, so treat that edge, and every module that imports through it, as broken output, not as a missing export to look for elsewhere. - With
--diagnostics, anes_source_kept_commonjswarning (non-error) names a module that stayed CommonJS although its input looks compiled from an ES module, with the evidence and the kept requires. Its imports are stillrequirecalls; follow them as imports. - A
.cjsor.ctsinput is decompiled as CommonJS: itsrequire()calls andmodule.exportsstay in place, so its output has no imports to follow.--unpackdoes not apply this yet: a.cjsinput or recovered module can still come out with ESM syntax, which Node cannot load from a.cjsfile. - Before rebuilding unpack output with a bundler, check ESM modules that
default-import a CommonJS sibling setting
__esModule: Node readsmodule.exports, a bundler readsexports.default(usuallyundefined). Rename every file without module syntax (import,export,import.meta, or top-levelawait) to.cjs, scripts included, update the specifiers, and add{"type": "module"}at the output root. - A
runtime_residualwarning (non-error) means that module still calls a bundler runtime helper the output does not define, such as__turbopack_context__.L(...)(Turbopack chunk loading),__turbopack_context__.P(...)(path resolution), or webpack'srequire.e/require.taround a lazyimport(), or a webpackrequire(id)whose module id no output links (the message says whether the id is in no input, in an opaque module, or in several modules of the build). Read the module normally; only that call is unresolved. For webpack, check that the chunk file holding the loaded module is among the inputs, once. failedin unpack JSON counts distinct modules with at least one error-class warning (decompile failures, declaration conflicts, output parse failures);totalis the module count.failed > 0always comes with a non-zero exit. Whenfailed < total, read the other modules normally and report the failed ones.- 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.