Back to Blog
Guide10 min read

How to Recover Lost Node.js Source Code From a Build (2026)

JSC Decompiler Team

Engineering

The source code is gone. The laptop died, the repository was deleted, the contractor who built the thing stopped answering email, or a wiped drive took the only copy with it. What you still have is the thing you shipped — a production build, a desktop app, a standalone binary, a single .jsc file someone emailed you two years ago.

Here is the good news: if you have the build, you can almost certainly recover your Node.js source code. JavaScript that ships to production is rarely destroyed — it is transformed. Minified, bundled, compiled to bytecode, packed into an archive. Every one of those transformations is reversible to some degree, and the closer the transformation is to “just compiled,” the more faithfully you get your code back. This guide walks through recovering working JavaScript from whatever artifact survived.

How to recover lost Node.js source code, in short: identify the surviving build artifact, then work it back to JavaScript by the most faithful route available —

  1. Source maps (.js.map) rebuild your original tree almost perfectly.
  2. Minified or bundled JavaScript just needs reformatting and unbundling.
  3. Compiled V8 bytecode (.jsc) decompiles back to working JavaScript.
  4. Electron apps and single executables (pkg, nexe, SEA) need the container extracted first, then one of the above.

This is your code. Recovering source from a build you own or are authorized to maintain is a normal engineering task — disaster recovery, not reverse engineering. The same techniques work either way, but the framing matters: you are restoring an asset you have the right to restore.

First, Figure Out What You Actually Have

Recovery fidelity depends entirely on the form your code is in. Before you do anything else, inventory the surviving artifact and figure out which rung of the ladder you're on. Run a quick triage:

  • Are there .js.map files next to the JavaScript? Best case. Source maps can rebuild your original tree almost perfectly.
  • Are there plaintext .js files, even minified ones? Good case. The logic is all there; it just needs reformatting.
  • Are there .jsc files, or a binary that runs but contains no readable JavaScript? Recoverable case. That's compiled V8 bytecode, and it decompiles back to JavaScript.
  • Is it a desktop app (Electron) or a single executable (pkg, nexe, SEA)? Extract first, then recover. The code is inside a container you need to open before you can read it.

Most real recoveries are a combination — an Electron app whose main process is compiled to bytecode while the renderer is a minified webpack bundle, for example. Work through each artifact on its own.

The Recovery Ladder, Best Case to Hardest

1. Source Maps — Near-Perfect Recovery

If your build pipeline emitted source maps and they shipped (or were archived) alongside the bundle, stop reading and start celebrating. A .js.map file contains the original file paths, the original source content, and a position-by-position mapping back to it. Tools that consume source maps will reconstruct your original directory structure, variable names, and in many cases comments — the closest thing to having the repository back.

# A source map embeds the original sources verbatim
$ cat dist/app.js.map | python3 -m json.tool | head
{
    "version": 3,
    "sources": [
        "../src/server.ts",
        "../src/auth/session.ts",
        "../src/db/pool.ts"
    ],
    "sourcesContent": [
        "import express from 'express';\n..."   # <- your original code
    ]
}

Check the bundle's last line for a //# sourceMappingURL= comment, look for .map files in the build output, and check your CI artifact storage — maps are often uploaded for error tracking (Sentry, Datadog) even when they aren't shipped to users.

2. Minified or Bundled JavaScript — Reformat and Read

No source maps, but plaintext JavaScript? The logic is fully intact. Minification renames variables and strips whitespace, but it never removes behavior. A beautifier restores structure, and modern unbundlers can split a single webpack/rollup blob back into its component modules.

# Reformat a minified bundle
$ npx prettier --write dist/app.js

# Split a bundled blob back into modules and undo common transforms
$ npx webcrack dist/app.js -o recovered/

You'll get readable code with mechanical variable names (a, e, t) that you rename back as you re-learn the code.

3. Compiled V8 Bytecode (.jsc) — Decompile

This is the case most people assume is hopeless, and it isn't. A .jsc file — usually produced by bytenode — is serialized V8 bytecode, not encryption. It is a deterministic translation of your JavaScript into V8's instruction set, and that translation runs backward. A decompiler reconstructs control flow, string literals, function structure, and API calls. The next section covers this end to end, because it's the path that recovers the most code from the least obvious artifact — and if you want the mechanics in isolation, our walkthrough on how to decompile .jsc files to JavaScript covers the available methods.

4. Inside an Electron App — Open the Container First

If what survived is the installed desktop app, your code is inside an app.asar archive. Extract it, then handle each module by whichever rung it lands on — plaintext, minified, or compiled.

# Find and extract the ASAR (macOS path shown)
$ npx @electron/asar extract \
    "/Applications/YourApp.app/Contents/Resources/app.asar" ./recovered

$ find ./recovered -name "*.jsc" -o -name "*.js" | head

The full walkthrough — per-platform ASAR locations, unpacked directories, custom archive names — is in our guide to decompiling Electron apps.

5. Inside a Single Executable (pkg, nexe, SEA)

Standalone binaries built with pkg, nexe, or Node's Single Executable Application feature embed your application — often as compiled bytecode — inside the binary. The runtime, your assets, and a blob of V8 bytecode are concatenated together. Carve out the bytecode blob, then decompile it the same way as a loose .jsc file.

How to Recover Node.js Source Code From a .jsc File

This is the core recovery path — the one that turns a file you can't open into JavaScript you can maintain.

Step 1: Locate Every Compiled File

Inventory the build. Compiled files usually carry a .jsc extension, but developers rename them — .bin, .dat, even .node. Look for files that file reports as raw data and that contain no readable JavaScript.

Step 2: Confirm It's Bytecode

Open the file in a hex viewer. V8 bytecode starts with a binary header and shows scattered string fragments — your URLs and error messages — embedded in otherwise binary data. That mix of binary structure with recognizable strings is the signature of compiled code, and it's exactly what makes recovery possible.

$ file dist/core.jsc
dist/core.jsc: data

$ strings dist/core.jsc | head
api/v2/sync
DATABASE_URL
Invalid credentials
SELECT * FROM accounts WHERE id = ?

Step 3: Determine the V8 Version (or Let It Auto-Detect)

V8 bytecode is version-specific — the format changes with nearly every major Node.js release, so a decompiler has to match the version that produced the file. If you remember which Node version the project used, check the old package.json engine field or any bundled Node binary. If you don't — which is common when the whole project is gone — JSC Decompiler reads the version straight from the bytecode header and selects the right parser automatically.

Step 4: Decompile

Upload the file to JSC Decompiler. It parses the header, identifies the V8 version, walks the bytecode instruction stream, and emits JavaScript. Here is a representative before-and-after for a recovered server module:

The file you have:

c6c6 148a 0d00 0000 5ca1 2f67 0000 0000
0000 0000 d804 0000 f147 0d00 0800 0000
...  (binary data, 38 KB, unreadable)

The code you get back:

// Decompiled from: core.jsc
// Detected: V8 11.3 (Node.js 20.x)

const express = require("express");
const { Pool } = require("pg");

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

function registerRoutes(app) {
  app.post("/api/v2/sync", async (req, res) => {
    const account = await pool.query(
      "SELECT * FROM accounts WHERE id = $1",
      [req.body.accountId]
    );

    if (account.rows.length === 0) {
      return res.status(401).json({ error: "Invalid credentials" });
    }

    const updated = await applyDelta(account.rows[0], req.body.delta);
    res.json({ synced: true, version: updated.version });
  });
}

module.exports = { registerRoutes };

The route, the query, the status codes, the response shape — all back. Repeat for every compiled file and you have reassembled the application. For a build with dozens of modules, the batch upload and the REST API let you recover the whole tree in one pass instead of file by file. If you'd rather compare your options first, see how JSC Decompiler stacks up against View8 and ghidra_nodejs.

What You Get Back — and What You Don't

Decompiled code is functionally equivalent to your original, but it isn't byte-for-byte identical. Set expectations correctly so you know what cleanup is ahead.

Recovered Intact

  • Control flow — every branch, loop, and try/catch, rebuilt from the jump instructions.
  • Strings and constants — URLs, queries, config keys, error messages, magic numbers, regexes, all verbatim.
  • Function and class structure — the call graph, method bodies, exports, and imports.
  • Function, method, and property names — V8 keeps these because it needs them at runtime.

Not Recovered

  • Comments — stripped at compile time; they only ever lived in the source.
  • Local variable names — replaced with register references; the decompiler assigns synthetic names you'll rename.
  • Formatting — indentation and line breaks are regenerated, not original.
  • TypeScript types — if the project was TypeScript, you recover the compiled JavaScript, not the annotations.

The practical takeaway: you recover a working program, not a pristine repository. The logic that took months to write is back; the polish that takes an afternoon — renaming variables, reformatting, re-adding comments — is on you.

Putting the Recovered Code Back Into Shape

Once you have JavaScript back, a short cleanup pass turns it into a codebase you can maintain again:

  • Reformat everything — run prettier --write across the recovered tree so the whole project shares one consistent style.
  • Rename as you read — synthetic names like v0 and r1 become meaningful the moment you understand a function. Rename incrementally; don't try to do it all up front.
  • Restore module boundaries — recreate the directory structure the imports imply, so the project looks like a project again.
  • Get it building and running — reconstruct package.json from the require() calls you see, install the dependencies, and confirm the recovered code runs against the same inputs as the build.
  • Commit immediately — initialize a fresh git repository and commit the raw recovery before you change anything, so you always have the known-good baseline to diff against.

Real Recovery Scenarios

The Contractor Who Disappeared

You paid for a Node.js service or an Electron tool, you got a working build, and you never got the repo. Now the contractor is unreachable and you need to fix a bug or add a feature. The deliverable you own contains the code — extract the artifact, decompile the compiled modules, and you have a starting point to maintain it yourself.

The Dead Build Server

The CI server that held the only checkout died, and the git remote was on the same box. But the last release is still deployed and running in production. Pull the artifact off the running host and recover the source from it — the deployed build is your backup.

The Wiped Repository

Ransomware, a fat-fingered force push, or an account closure took the repository and its history. If you have any shipped version — an installer, a Docker image layer, a desktop app on someone's machine — the code inside it is recoverable, even if it's a few releases behind.

The Acquisition With No Handoff

You acquired a product or a team and inherited the running software but not a clean source handoff. Recovering the source from the build gets you to a maintainable state while the paperwork and access catch up.

How to Never Be Here Again

Once the code is back, make recovery unnecessary next time:

  • Push to at least two independent git remotes — never let the only copy live on one machine or one provider.
  • Archive build artifacts and source maps to private storage on every release, with retention you control.
  • Automate an off-site backup of the repository, not just the deploy target.
  • Make sure every contractor engagement ends with a source handoff to a repo you own, before final payment.

A reminder that cuts both ways: because shipped builds are recoverable, compiling your code to bytecode is not a substitute for protecting it. If readable source in the wrong hands would hurt you, see what attackers actually recover from a build.

Summary

Lost source code is usually recoverable as long as the build survived. Source maps give you back nearly everything; minified bundles need only reformatting; compiled .jsc bytecode decompiles back to working JavaScript; and code locked inside an Electron app or a single executable just needs the container opened first. You recover control flow, strings, and structure intact — comments, original variable names, and formatting are the cleanup you do afterward.

If your surviving artifact is compiled bytecode, JSC Decompiler detects the V8 version automatically and reconstructs the JavaScript — the fastest path from a file you can't open back to a codebase you can maintain.

Frequently Asked Questions

Can you recover source code from a compiled .jsc file?

Yes. A .jsc file is serialized V8 bytecode, not encryption — a deterministic translation of your JavaScript that runs backward. Decompiling it reconstructs working JavaScript with control flow, string literals, function names, and API calls intact. Comments, local variable names, and original formatting are the only parts that don't survive.

Is it legal to recover your own source code?

Recovering source from software you own or are authorized to maintain is ordinary disaster recovery, not reverse engineering. Decompiling code you don't own is a separate question governed by the software's license and the laws of your jurisdiction — so confirm you have the right to the artifact before you start.

Can deleted source code be recovered if the repository is gone?

Often, yes. If any shipped build survived — a running deployment, an installer, a Docker image layer, or a desktop app on someone's machine — the code inside it is recoverable even when the repository and its entire git history are gone. The recovered version will match whichever release that artifact was built from.

Does compiling Node.js code to bytecode delete the original source?

No. Compilation translates your source into V8 bytecode and stores it in the build; the program logic is fully preserved, which is exactly why it can be decompiled back to readable JavaScript. The original source files may be gone from your machine, but the code itself still lives inside the compiled artifact.

Try JSC Decompiler Free

Upload a .jsc file and get readable JavaScript back in seconds. No signup required for the free tier.