Coder Social home page Coder Social logo

pionxzh / wakaru Goto Github PK

View Code? Open in Web Editor NEW
178.0 2.0 9.0 2.59 MB

πŸ”ͺπŸ“¦ Javascript decompiler for modern frontend

Home Page: https://wakaru.vercel.app/

License: MIT License

JavaScript 40.18% TypeScript 56.05% HTML 0.11% Vue 2.54% CSS 1.06% Dockerfile 0.05% Batchfile 0.01% Shell 0.01%
ast babel debundle decompiler javascript reverse-engineering swc unminify unpack webpack browserify jscodeshift

wakaru's Introduction

Wakaru

deploy codecov Telegram-group

Wakaru is the Javascript decompiler for modern frontend. It brings back the original code from a bundled and transpiled source.

  • πŸ”ͺπŸ“¦ Unpacks bundled JavaScript into separated modules from webpack and browserify.
  • β›οΈπŸ“œ Unminifies transpiled code from Terser, Babel, SWC, and TypeScript.
  • βœ¨πŸ“š Detects and restores downgraded syntaxes (even with helpers!). See the list.
  • πŸ§ͺπŸ›‘οΈ All cases are protected by tests. All code is written in TypeScript.

Demo

See live demo for detailed examples.

Features

Unminify

Converts transpiled code back to its readable form and restores downgraded syntaxes.

Supports the following transpilers:

  • Terser
  • Babel
  • SWC
  • TypeScript

Read the documentation for more information.

Unpacker

Converts bundled JavaScript into separated modules

Supports the following bundlers:

  • webpack
  • browserify

Try it out

Test the tool and see it in action at Playground.

πŸ–₯ Using the CLI

Interactive mode

By default, the CLI will run in interactive mode and guide you through the process.
You can also pass options to skip some steps in the interactive mode.

npx @wakaru/cli
# or
pnpm dlx @wakaru/cli

Options

Run npx @wakaru/cli --help to see the full list of options.

Option Default Description
--output "out" Output directory
--force false Force overwrite output directory
--concurrency 1 Specific the number of concurrent tasks
--perf false Show performance metrics
--perf-output Performance metrics output directory

--concurrency can be used to speed up the process. But please aware that the process might OOM if the input file is too large.

Non-interactive mode

If you want to run the CLI in non-interactive mode, you can specify the feature by passing the feature name as the first argument.

unpacker and unminify will run only the corresponding feature.
all will run both unpacker and unminify sequentially.

npx @wakaru/cli all      <files...> [options]
npx @wakaru/cli unpacker <files...> [options]
npx @wakaru/cli unminify <files...> [options]

These options are only available in all mode.

Option Default Description
--unpacker-output "out/unpack" Override unpacker output directory
--unminify-output "out/unminify" Override unminify output directory

When running a single feature (either unpacker or unminify), the CLI will only uses the path specified in the --output option. This means that, unlike in the all mode where subdirectories (out/unpack and out/unminify) are automatically created within the output directory, in single feature mode, the output files are placed directly in the specified --output directory without any additional subdirectories.

πŸ“¦ Using the API

npm install @wakaru/unpacker @wakaru/unminify
# or
pnpm install @wakaru/unpacker @wakaru/unminify
# or
yarn add @wakaru/unpacker @wakaru/unminify
Click to expand

@wakaru/unpacker

import { unpack } from '@wakaru/unpacker';

const { modules, moduleIdMapping } = await unpack(sourceCode);
for (const mod of modules) {
  const filename = moduleIdMapping[mod.id] ?? `module-${mod.id}.js`;
  fs.writeFileSync(outputPath, mod.code, 'utf-8');
}

@wakaru/unminify

import { runDefaultTransformationRules, runTransformationRules } from '@wakaru/unminify';

const file = {
  source: '...', // source code
  path: '...',   // path to the file, used for advanced usecases. Can be empty.
}
// This function will apply all rules that are enabled by default.
const { code } = await runDefaultTransformationRules(file);

// You can also specify the rules to apply. Order matters.
const rules = [
  'un-esm',
  ...
]
const { code } = await runTransformationRules(file, rules);

You can check all the rules at /unminify/src/transformations/index.ts.

Please aware that this project is still in early development. The API might change in the future.

And the bundle size of these packages are huge. It might be reduced in the future. Use with caution on the browser (Yes, like the playground, it can run on the browser ✨).

Legal Disclaimer

Usage of wakaru for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.

License

MIT

wakaru's People

Contributors

ahmadfaraz-crypto avatar anka-213 avatar g-plane avatar github-actions[bot] avatar kiangkuang avatar pionxzh avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

wakaru's Issues

[Pattern πŸ€”] Unknown enum-like pattern

I noticed a pattern that looks like an enum but is much simpler. And TS Enum does not support boolean as the value.

The code was probably generated by some tools, as most people shouldn't write code like this.

Not sure what's this πŸ€”

var n =
  ((a = {}),
  (a[34] = true),
  (a[20] = true),
  (a[31] = true),
  (a[22] = true),
  (a[71] = true),
  a
);

Support detect and replace `swc`'s `runtime-helpers`

Spinning this out into a new issue so it doesn't get lost among the old one, but follow through to the original comment for more of the deep dive/evidence that lead to this being figured out:

It seems that the issue here is less about smart-rename's handleReactRename not handling useState properly; and more that wakaru needs to add support for swc's 'runtime helper' functions like _sliced_to_array / etc from @swc/helpers; probably in a similar way to how babel's are currently implemented:

This may in part be relevant to the following 'module detection' issue as well:

Originally posted by @0xdevalias in #49 (comment)


See Also

add Chrome DevTools extension that allows the web IDE to be used within DevTools

This is an idea I've been thinking of exploring for a while, and recently articulated on another unminimiser. Wanted to raise it here as well in case it's useful, as I feel like it could be a really cool tool that improves the workflow of exploring/unminimising webapps that would fit well with wakaru.

This issue is about an idea I've had and wanted to work on myself for a while, but haven't got around to it yet. Often when exploring web apps, I make pretty heavy use of Chrome DevTools' 'Search' / 'Sources' / 'Network' / etc tabs, the debugger, and the console / utilities APIs. While there have been some nice improvements to the 'Sources' tab over the years (pretty printing, syntax highlighting, code folding, etc); one area I have really wished it was able to support for a long time now is 'references' / 'go to definition' / similar.

A thought I had in this space is that, while I obviously can't outright replace the 'Sources' tab (which I believe is based on CodeMirror), it should be possible to create a Chrome DevTools Extension that adds a new tab/panel that does similar to the current 'Sources' tab, but using monaco as it's base, and enabling features like 'references' / 'go to definition' / etc within that.

Useful Chrome Extension APIs

Overview of extending DevTools:

Some of the Chrome Extension API's that would be useful/enable this:

Then there are also all of the 'standard' Chrome Extension APIs as well, which can do a lot of cool stuff:

A few of which could be useful for this feature:

And some that are a little more esoteric, but might still be interesting/useful:

Originally posted by @0xdevalias in j4k0xb/webcrack#29


Edit: This is also captured on the following gist for posterity, and that will likely be where I capture any future notes about my own explorations of this:

add `/*wakaru:missing*/` annotations to imports from missing modules/files

I was just testing the new webcrack web IDE (Ref), and noticed that they annotate the unminified source to explicitly mention when a module being imported from is missing with /*webcrack:missing*/. This would be a cool feature for wakaru to support as well.

You can see the full context of how I got to this unminified step on the issue I opened there (Ref), using this minified code (Ref), but here is what the annotation looks like in that unminified code:

webcrack's 180.js with the /*webcrack:missing*/ annotation:

require.d(exports, {
  Z: function () {
    return a;
  }
});
var r = require( /*webcrack:missing*/"./35250.js");
// ..snip..

Compared to wakaru's unminified module-180.js without it:

const { jsx } = require(35250);

// ..snip..

export default {
  Z: a,
};

`un-esm` is not transforming `module.exports.default = module.exports` correctly

Describe the bug

un-esm didn't generate the correct export with module.exports.default = module.exports.

And there is also a redundant __esModule which should be removed.

Input code

function B() { }
module.exports = B;
module.exports.__esModule = !0;
module.exports.default = module.exports;

Reproduction

No response

Steps to reproduce

No response

Expected behavior

function B() { }
export default B;

Actual behavior

function B() { }
export const __esModule = true;
export default module.exports;

Split code and save progress? OOM?

originally from #35

I have a 5Mb sized file that needs to be processed, I've tried running it directly in node and it takes up to 21G of memory space in some transform processing.

However, in some of the processing, it may fail, so maybe it would be possible to save the data that was processed successfully? And use it again next time?

This issue is used to track things we need to improve to prevent:

  1. Lossing data when the unminified code is larger than 5MB (the size limit from LocalStorage)
  2. OOM
  3. Save the data more frequently to avoid the transformation failing half way and losing all the code we get
  • #33
  • When the input is larger than a threshold, give user a warning that it might cause OOM
  • Performance improvement to reduce the memory usage and improve the speed.
    #35
  • Save the data more frequently

Centralize CLI to `@wakaru/cli`

As the title said, I want to move the CLI part to a standalone /cli package. The CLI was implemented in #37, both unpacker and unminifiy have their own CLI.

This brought some drawbacks. First, CLI-related dependencies are added to the dependencies of the package. This is not good for the package user (if there are any). Second, because of the current bad abstraction, issue #62 happened. These architectural things will be fixed eventually, but I still want to move them into a single CLI entry.

Old CLI will be deprecated, printing messages to let users migrate to the new CLI.

Over-shadowing of 'code' Variable in startUnpack Function's map Callback in packages/playground/src/pages/Uploader.vue

I was looking at the playground package to understand how the unpack code was being called/what it's API looked like/etc. While looking at the startUnpack code, I noticed what looks like it could be a bug.

In the startUnpack function, there is an issue with the shadowing of the code variable. The function destructures code from the module object, which overshadows the code variable from the higher scope of the startUnpack function.

The issue occurs in the startUnpack function, specifically within the map callback applied to modules:

https://github.com/pionxzh/wakaru/blob/b06d0a7d8682042700398f0bbba1d76839fb57cf/packages/playground/src/pages/Uploader.vue#L80C1-L99C7

Expected Behavior

We expect each module to be transformed into an object with distinct properties, where code is the original code from the module, and another property (possibly named transformed) for the processed or transformed code, without overshadowing the original code variable from the higher scope.

Suggested Fix

To fix this, we should directly destructure a transformed (or similarly named) property from the module object, avoiding the overshadowing of the code variable. For example:

    const { modules, moduleIdMapping } = unpack(code)
    const unpackedModules = modules.map<TransformedModule>((module) => {
-       const { id, isEntry, code, tags } = module
+       const { id, isEntry, code: transformed, tags } = module
        return {
            id,
            isEntry,
            code,
-           transformed: code,
+           transformed,
            import: module.import,
            export: module.export,
            tags,
        }
    })

Consider storing data into IndexedDB

If you try to parse a huge js file in the playground, it will hit localstorage's 5MB limitation. The whole processing is still ok, but files extracted won't be successfully persisted.

CLI: show progress/file paths while unpacking/unminifying

Currently the CLI doesn't seem to show any indication of progress while unpacking (possibly also while unminifying?):

β‡’ npx @wakaru/cli unpacker unpacked --unpacker-output stage2-unpacked --perf

β”Œ   Wakaru CLI v0.0.2
β”‚
β””  Selected features: Unpacker

β”Œ   Unpacker
β”‚
β—‡  Unpacking...

It would be nice if we were able to see some more output here; potentially a progress bar, and/or list of the file paths being processed. This would help with understanding what the CLI is currently doing, if it's frozen, etc.

It would also make it easier to identify what file was likely the cause of an error like this, as we could then manually correlate the path of the file being processed with the error message:

SyntaxError: Leading decorators must be attached to a class declaration. (1:5)
    at constructor (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:18018:23)
    at FlowParserMixin.raise (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:20914:23)
    at FlowParserMixin.parseDecorators (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:30573:22)
    at FlowParserMixin.parseStatementLike (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:30370:29)
    at FlowParserMixin.parseStatementLike (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:22799:28)
    at FlowParserMixin.parseModuleItem (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:30349:21)
    at FlowParserMixin.parseBlockOrModuleBlockBody (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:30976:40)
    at FlowParserMixin.parseBlockBody (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:30969:14)
    at FlowParserMixin.parseProgram (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:30248:14)
    at FlowParserMixin.parseTopLevel (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/chunk-KOQHKJQW.cjs:30238:29) {
  code: 'BABEL_PARSER_SYNTAX_ERROR',
  reasonCode: 'UnexpectedLeadingDecorator',
  loc: Position { line: 1, column: 5, index: 5 },
  pos: 5
}

There may be other ways to improve error outputs like that as well (eg. potentially being able to inform the parser of the path/filename of the code being processed, and then it may expose that info directly in the error messages)


It appears that the CLI is currently built with clack:

That seems to have the concept of a spinner, but seemingly not a progress bar/etc:

It can apparently even do multiple tasks in spinners:

From a quick google (in no particular order):

add easy-to-use CLI features

  1. Automatically catch exceptions and create URLs where issues can be submitted
  2. Provide some non-privacy related data collection for refining parsing rules
  3. cli support i18n and configuring memory

Scoping issue

The current identifier renaming is not 100% accurate. By inspecting the unpacking snapshot, you can tell that some variable was wrongly renamed to export or require during the unpacking process. Mostly because ast-types are giving us wrong scope information, and it's no longer well-maintained. We need to either patch it or find an alternative.

The best solution would be to fix it in the upstream.
Let's track the progress at facebook/jscodeshift#500

refactor 'leaky abstractions' related to ASTs + other AST utils into @wakaru/ast-utils

Extracting this from a deeper comment in another issue so it doesn't get 'lost among the noise' there:

For my own reference, here are the code locations currently referencing jscodeshift, recast and/or ast-types:

I also note that there is a @wakaru/ast-utils package that could also potentially be used to centralise some 'leaky abstractions' from these libs if we were to refactor:

And it might also potentially make sense to refactor some of these utils into that ast-utils package as well maybe?

Originally posted by @0xdevalias in #32 (comment)

[module-detection] DataDog/browser-sdk

This relates to the 'module-detection' feature described in the following issue:

Overview

Code

Unminifying this source (Ref), module-15300.js:

Note: Not including the unminified examples here currently as the file is quite large

Searching for some of the symbols from module-15300.js on GitHub code search:

Of those results, this looks the most promising:


Using the madge CLI/lib that I mentioned in another issue (Ref), I was able to automatically find/generate a graph of all of the module dependencies related to this.

I narrowed down on this by first creating a larger image of all the dependencies in a chunk/etc, finding something that looked like a lib like this, then running it again on that module.

Note also that I haven't manually checked through all of the modules mentioned here yet.

.madgerc
// See: https://github.com/pahen/madge#configuration
{
  "fileExtensions": ["js"],
  "detectiveOptions": {
    "es6": {
      "mixedImports": true,
      "skipTypeImports": true
    },
    "ts": {
      "mixedImports": true,
      "skipTypeImports": true
    }
  }
}
β‡’ npx madge --image 15300.svg stage3-unminified/pages/_app/module-15300.js

15300

(blue has dependencies, green doesn't)

madge --json output
β‡’ npm run-script madge:json stage3-unminified/pages/_app/module-15300.js

> [email protected] madge:json
> f() { MODULE_PATH="$1"; npx madge --json "$MODULE_PATH"; }; f stage3-unminified/pages/_app/module-15300.js

{
  "module-15300.js": [
    "module-44675.js",
    "module-49406.js",
    "module-53596.js",
    "module-66816.js"
  ],
  "module-39730.js": [],
  "module-44675.js": [],
  "module-49406.js": [
    "module-39730.js"
  ],
  "module-53596.js": [
    "module-39730.js"
  ],
  "module-66816.js": []
}

BABEL_PARSER_SYNTAX_ERROR NumberIdentifier SyntaxError: Identifier directly after number

Attempting to unpack/unminify some webpacked code (Ref):

β‡’ cd ./unpacked/_next/static/chunks

β‡’ npx @wakaru/unpacker pages/_app.js -o ./pages/_app-unpacked/
Generated 213 modules from pages/_app.js to pages/_app-unpacked (24,049ms)

β‡’ npx @wakaru/unminify ./pages/_app-unpacked/* -o ./pages/_app-unminified
# ..snip..

I got the following error while attempting to unminify:

# ..snip..

β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-96733.js (900.5ms)
SyntaxError: Identifier directly after number. (121:3)
    at instantiate (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:17646:36)
    at constructor (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:17942:16)
    at FlowParserMixin.raise (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:20820:23)
    at FlowParserMixin.readNumber (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:20677:22)
    at FlowParserMixin.getTokenFromCode (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:20431:18)
    at FlowParserMixin.getTokenFromCode (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:24601:15)
    at FlowParserMixin.getTokenFromCode (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:22998:17)
    at FlowParserMixin.nextToken (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:19935:14)
    at FlowParserMixin.next (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:19845:14)
    at FlowParserMixin.parseObjectLike (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:29309:14) {
  code: 'BABEL_PARSER_SYNTAX_ERROR',
  reasonCode: 'NumberIdentifier',
  loc: Position { line: 121, column: 3, index: 1649 },
  pos: [Getter/Setter]
}
  119 | };
  120 | var tE = {
  121 |   2D: 0,
          ^
  122 |   WebGL: 1,
  123 |   WebGL2: 2,
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-96837.js (27,929.5ms)
# ..snip..

Port transform passes from synchrony

Synchrony is a deobfuscator for javascript-obfuscator and js-confuser.

It provides a few useful passes:

  • DeadCode (Dead code elimination)
  • ControlFlow (defeat CFG flattening)
  • StringDecoder (decrypt string constants)
  • etc.

However, it directly operates on Acorn AST trees, and isn't very actively maintained. It would be a big benefit for us to port the passes here.

We might want to ask the author for a relicense, since it's GPL-3.0-only.

[module-detection] TanStack/query

This relates to the 'module-detection' feature described in the following issue:

Overview

  • https://github.com/TanStack/query
    • Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.

    • Hooks for fetching, caching and updating asynchronous data in React, Solid, Svelte and Vue

Code

Unminifying this source (Ref), module-24396.js/module-55659.js/module-57457.js/etc:

Note: Not including the unminified examples here currently as the file is quite large

Searching for some of the symbols from module-55659.js/module-57457.js on GitHub code search:

Of those results, these look the most promising:


Using the madge CLI/lib that I mentioned in another issue (Ref), I was able to automatically find/generate a graph of all of the module dependencies related to this.

I narrowed down on this by first creating a larger image of all the dependencies in a chunk/etc, finding something that looked like a lib like this, then running it again on that module.

Note also that I haven't manually checked through all of the modules mentioned here yet.

.madgerc
// See: https://github.com/pahen/madge#configuration
{
  "fileExtensions": ["js"],
  "detectiveOptions": {
    "es6": {
      "mixedImports": true,
      "skipTypeImports": true
    },
    "ts": {
      "mixedImports": true,
      "skipTypeImports": true
    }
  }
}
β‡’ npx madge --image 24396.svg stage3-unminified/pages/_app/module-24396.js

24396

(blue has dependencies, green doesn't)

madge --json output
β‡’ npm run-script madge:json stage3-unminified/pages/_app/module-24396.js

> [email protected] madge:json
> f() { MODULE_PATH="$1"; npx madge --json "$MODULE_PATH"; }; f stage3-unminified/pages/_app/module-24396.js

{
  "module-1371.js": [
    "module-53729.js"
  ],
  "module-16456.js": [],
  "module-24396.js": [
    "module-16456.js",
    "module-55659.js",
    "module-57457.js"
  ],
  "module-28189.js": [],
  "module-28197.js": [],
  "module-31178.js": [],
  "module-35448.js": [],
  "module-36665.js": [],
  "module-37540.js": [
    "module-16456.js",
    "module-28197.js"
  ],
  "module-53729.js": [],
  "module-55659.js": [
    "module-1371.js",
    "module-28189.js",
    "module-35448.js",
    "module-36665.js",
    "module-59463.js",
    "module-67531.js",
    "module-84427.js"
  ],
  "module-57457.js": [
    "module-16456.js",
    "module-28197.js",
    "module-84427.js",
    "module-94402.js",
    "module-99475.js"
  ],
  "module-59463.js": [],
  "module-67531.js": [
    "module-31178.js"
  ],
  "module-84427.js": [
    "module-16456.js"
  ],
  "module-94402.js": [
    "module-16456.js",
    "module-37540.js",
    "module-99475.js"
  ],
  "module-99475.js": [
    "module-16456.js",
    "module-28197.js"
  ]
}

wakaru IDE

Figured I would split this off into a new issue so it doesn't flood the last unrelated issue too much.


Are you able to tell us more about your plan/roadmap with it, and what you're hoping it will allow/enable?

@0xdevalias I have created a package /packages/ide which provides a vscode-like experience. I want to use it to replace the existing playground. I am still trying to figure out the idea haha.. Module mapping won't be exposed, instead, you can directly edit the filename in the file explore. And two tabs will be provided for the compare view, which you can drag and organize in any form you like. The editor would be Monaco, which offers a better editing experience. Features like download, better unminify handling, error reporting...

But I might drop the idea and simply put Monaco into the existing playground.

Originally posted by @pionxzh in #35 (comment)

[module-detection] zustand - React state management

This relates to the 'module-detection' feature described in the following issue:

Overview

zustand is a state management library used in React apps:

  • https://github.com/pmndrs/zustand
    • Bear necessities for state management in React

    • A small, fast and scalable bearbones state-management solution using simplified flux principles. Has a comfy API based on hooks, isn't boilerplatey or opinionated.

Code

Unminifying this source (Ref):

Original (prettified)
// unpacked/_next/static/chunks/pages/_app.js, lines 58342-58372
    81292: function (U, B, G) {
      "use strict";
      G.d(B, {
        ZP: function () {
          return tt;
        },
        oR: function () {
          return X;
        },
      });
      var Y = G(97703),
        V = G(70079),
        Z = G(92280);
      let { useSyncExternalStoreWithSelector: J } = Z;
      function X(U, B = U.getState, G) {
        let Y = J(
          U.subscribe,
          U.getState,
          U.getServerState || U.getState,
          B,
          G
        );
        return (0, V.useDebugValue)(Y), Y;
      }
      let Q = (U) => {
          let B = "function" == typeof U ? (0, Y.Z)(U) : U,
            G = (U, G) => X(B, U, G);
          return Object.assign(G, B), G;
        },
        tt = (U) => (U ? Q(U) : Q);
    },
Source (unpacked)
// module-81292.js
"use strict";
var Y = require(97703);
var V = require(70079);
var Z = require(92280);
let { useSyncExternalStoreWithSelector: J } = Z;
function X(U, B = U.getState, G) {
  let Y = J(U.subscribe, U.getState, U.getServerState || U.getState, B, G);
  return (0, V.useDebugValue)(Y), Y;
}

let Q = (U) => {
  let B = "function" == typeof U ? (0, Y.Z)(U) : U;
  let G = (U, G) => X(B, U, G);
  return Object.assign(G, B), G;
};

let tt = (U) => (U ? Q(U) : Q);

module.exports = {
  ZP: tt,
  oR: X,
};

Transformed (unminified)

// module-81292.js
import Y, { Z as Z$0 } from "module-97703.js";
import Z from "module-92280.js";

const { useDebugValue } = require(70079);

let { useSyncExternalStoreWithSelector } = Z;
function X(U, B = U.getState, G) {
  let Y = useSyncExternalStoreWithSelector(
    U.subscribe,
    U.getState,
    U.getServerState || U.getState,
    B,
    G
  );
  useDebugValue(Y);
  return Y;
}

let Q = (U) => {
  let B = typeof U == "function" ? Z$0(U) : U;
  let G = (U, G) => X(B, U, G);
  Object.assign(G, B);
  return G;
};

let tt = (U) => (U ? Q(U) : Q);

export default {
  ZP: tt,
  oR: X,
};

Searching for some of those symbols on GitHub code search:

Of those results, this looks the most promising:

// zustand/src/react.ts, lines 7-10
import ReactExports from 'react'
import useSyncExternalStoreExports from 'use-sync-external-store/shim/with-selector'
import { createStore } from './vanilla.ts'
// zustand/src/react.ts, lines 18-19
const { useDebugValue } = ReactExports
const { useSyncExternalStoreWithSelector } = useSyncExternalStoreExports
  • For this code: const { useDebugValue } = require(70079);
    • module-70079.js is literally just module.exports = require(99504);
    • module-99504.js is react.production.min.js
// zustand/src/react.ts, lines 50-74
export function useStore(api, selector = api.getState, equalityFn) {
  // ..snip..
  const slice = useSyncExternalStoreWithSelector(
    api.subscribe,
    api.getState,
    api.getServerState || api.getState,
    selector,
    equalityFn,
  )
  useDebugValue(slice)
  return slice
}
// zustand/src/react.ts, lines 101-122
const createImpl = (createState) => {
  // ..snip..
  const api = typeof createState === 'function' ? createStore(createState) : createState

  const useBoundStore = (selector, equalityFn) => useStore(api, selector, equalityFn)

  Object.assign(useBoundStore, api)

  return useBoundStore
}

export const create = ((createState) => createState ? createImpl(createState) : createImpl)

From that, it seems the Z$0 function is createStore, which in this bundled code, is imported from 97703:

Original (prettified)
// unpacked/_next/static/chunks/pages/_app.js, lines 58373-L58400
    97703: function (U, B, G) {
      "use strict";
      G.d(B, {
        Z: function () {
          return V;
        },
      });
      let Y = (U) => {
          let B;
          let G = new Set(),
            Y = (U, Y) => {
              let V = "function" == typeof U ? U(B) : U;
              if (!Object.is(V, B)) {
                let U = B;
                (B = (null != Y ? Y : "object" != typeof V)
                  ? V
                  : Object.assign({}, B, V)),
                  G.forEach((G) => G(B, U));
              }
            },
            V = () => B,
            Z = (U) => (G.add(U), () => G.delete(U)),
            J = () => G.clear(),
            X = { setState: Y, getState: V, subscribe: Z, destroy: J };
          return (B = U(Y, V, X)), X;
        },
        V = (U) => (U ? Y(U) : Y);
    },
Source (unpacked)
// module-97703.js
"use strict";
let Y = (U) => {
  let B;
  let G = new Set();

  let Y = (U, Y) => {
    let V = "function" == typeof U ? U(B) : U;
    if (!Object.is(V, B)) {
      let U = B;
      (B = (null != Y ? Y : "object" != typeof V)
        ? V
        : Object.assign({}, B, V)),
        G.forEach((G) => G(B, U));
    }
  };

  let V = () => B;
  let Z = (U) => (G.add(U), () => G.delete(U));
  let J = () => G.clear();
  let X = { setState: Y, getState: V, subscribe: Z, destroy: J };
  return (B = U(Y, V, X)), X;
};

let V = (U) => (U ? Y(U) : Y);

module.exports = {
  Z: V,
};

Transformed (unminified)

// module-97703.js
let Y = (U) => {
  let B;
  let G = new Set();

  let Y = (U, Y) => {
    let V = typeof U == "function" ? U(B) : U;
    if (!Object.is(V, B)) {
      let U = B;

      B = (Y != null ? Y : typeof V != "object") ? V : Object.assign({}, B, V);

      G.forEach((G) => G(B, U));
    }
  };

  let V = () => B;
  let Z = (U) => {
    G.add(U);

    return () => G.delete(U);
  };
  let J = () => G.clear();
  let X = { setState: Y, getState: V, subscribe: Z, destroy: J };
  B = U(Y, V, X);
  return X;
};

let V = (U) => (U ? Y(U) : Y);

export default {
  Z: V,
};

Which based on the above, seems to correspond to:

// zustand/src/vanilla.ts, lines 60-106
const createStoreImpl = createState => {
  let state
  const listeners = new Set()

  const setState = (partial, replace) => {
    const nextState = typeof partial === "function" ? partial(state) : partial
    if (!Object.is(nextState, state)) {
      const previousState = state
      state =
        replace ?? (typeof nextState !== "object" || nextState === null)
          ? nextState
          : Object.assign({}, state, nextState)
      listeners.forEach(listener => listener(state, previousState))
    }
  }

  const getState = () => state

  const subscribe = listener => {
    listeners.add(listener)
    return () => listeners.delete(listener)
  }

  const destroy = () => {
    listeners.clear()
  }

  const api = { setState, getState, subscribe, destroy }
  state = createState(setState, getState, api)
  return api
}

export const createStore = createState =>
  createState ? createStoreImpl(createState) : createStoreImpl

BABEL_PARSER_SYNTAX_ERROR VarRedeclaration SyntaxError: Identifier 'tag' has already been declared

Attempting to unpack/unminify some webpacked code (Ref):

β‡’ cd ./unpacked/_next/static/chunks

β‡’ npx @wakaru/unpacker framework.js -o ./framework-unpacked/
Generated 9 modules from framework.js to framework-unpacked (3,800.3ms)

β‡’ npx @wakaru/unminify ./framework-unpacked/* -o ./framework-unminified

I got the following error while attempting to unminify:

# ..snip..
β€’ Transforming framework-unminified/framework-unpacked/module-2226.js (171.3ms)
SyntaxError: Identifier 'tag' has already been declared. (2491:14)
    at instantiate (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:17646:36)
    at constructor (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:17942:16)
    at FlowParserMixin.raise (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:20820:23)
    at FlowScopeHandler.checkRedeclarationInScope (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:19076:23)
    at FlowScopeHandler.declareName (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:19058:18)
    at FlowScopeHandler.declareName (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:19144:15)
    at FlowParserMixin.declareNameFromIdentifier (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:25112:20)
    at FlowParserMixin.checkIdentifier (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:25108:16)
    at FlowParserMixin.checkLVal (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:25046:16)
    at FlowParserMixin.checkLVal (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/@wakaru/unminify/dist/chunk-AY7YAENM.cjs:25078:18) {
  code: 'BABEL_PARSER_SYNTAX_ERROR',
  reasonCode: 'VarRedeclaration',
  loc: Position { line: 2491, column: 14, index: 54940 },
  pos: [Getter/Setter]
}
 2489 |           for (tag = r.return; tag !== null; ) {
 2490 |             var {
 2491 |               tag
                     ^
 2492 |             } = tag;
 2493 |             if ((tag === 3 || tag === 4) &&
β€’ Transforming framework-unminified/framework-unpacked/module-2920.js (59,842.4ms)
# ..snip..

Looking at module-2920.js, it's:

/**
 * @license React
 * react-dom.production.min.js
 *
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
version: "18.2.0",
rendererPackageName: "react-dom",
reconcilerVersion: "18.2.0-next-9e3b772b8-20220608",
(exports.version = "18.2.0-next-9e3b772b8-20220608");

CLI bug: Currently `--perf` doesn't seem to work when using `unpack` on it's own

It looks like --perf is only currently shown as part of the unminify code:

if (perf) {
printPerfStats(measurements)
writePerfStats(measurements, path.join(outputBase, 'perf.json'))
}

Ideally this would also be shown when running unpack on it's own as well.

In the case where both unpack and unminify are run together (eg. all), it would probably be interesting to be able to see the results for unpack, unminify, and combined (I haven't actually looked at what format these are currently displayed in, so this last part might not make sense)

[unminify] improve jsxs unminify when main tag is a ternary

Originally shared in #36 (comment)

Describe the bug

When attempting to unminify certain jsxs JSX code, I encountered an issue where the output does not correctly reflect the original structure. Specifically, a piece of code in module-63390.js seems to improperly handle the conditional selection of tags ("a" or "div").

Input code

Unminifying this source (Ref), and looking at module-63390.js

Reproduction

No response

Steps to reproduce

  1. Unminify the source code (Ref) and inspect module-63390.js.
  2. Observe the handling of the conditional tag selection, especially in the jsxs(url ? "a" : "div", {...}) part.

Expected behavior

Running it through ChatGPT, it suggested that a canonical way to implement that pattern would be to seperate the ternary out from the main JSX block into a 'helper tag', similar to this:

// Helper function for conditionally setting tag type
const Tag = url ? 'a' : 'div';

// Dynamic class names
const classNames = Z$0(
  "flex h-full w-full flex-col overflow-hidden rounded-md border border-black/10 bg-gray-50 shadow-[0_2px_24px_rgba(0,0,0,0.05)]",
  className
);

return (
    <Tag
      className={classNames}
      href={url}
      target={url ? "_blank" : ""}
      onClick={h}
    >
    //..snip..

Actual behavior

Source (unpacked)

// module-63390.js, lines 186-225
  return (0, o.jsxs)(r ? "a" : "div", {
    className: (0, l.Z)(
      "flex h-full w-full flex-col overflow-hidden rounded-md border border-black/10 bg-gray-50 shadow-[0_2px_24px_rgba(0,0,0,0.05)]",
      s
    ),
    href: r,
    target: r ? "_blank" : "",
    onClick: h,
    children: [
      c &&
        (0, o.jsx)(H, {
          children: (0, o.jsx)("div", {
            className: "absolute inset-0",
            children: (0, o.jsx)("img", {
              src: a,
              alt: "image of ".concat(n),
              className: "h-full w-full border-b border-black/10 object-cover",
            }),
          }),
        }),
      (0, o.jsxs)("div", {
        className: "flex flex-1 flex-col justify-between gap-1.5 p-3",
        children: [
          (0, o.jsx)(z, { $clamp: (void 0 !== u && u) || c, children: n }),
          (0, o.jsxs)("div", {
            className: "flex items-center gap-1",
            children: [
              i
                ? (0, o.jsx)(R.Z, { url: i, name: t, size: 13 })
                : (0, o.jsx)(U.Z, { url: r, size: 13 }),
              (0, o.jsx)("div", {
                className: "text-[10px] leading-3 text-gray-500 line-clamp-1",
                children: t,
              }),
            ],
          }),
        ],
      }),
    ],
  });

Transformed (unminified)

// module-63390.js, lines 213-252
return jsxs(url ? "a" : "div", {
    className: Z$0(
      "flex h-full w-full flex-col overflow-hidden rounded-md border border-black/10 bg-gray-50 shadow-[0_2px_24px_rgba(0,0,0,0.05)]",
      className
    ),
    href: url,
    target: url ? "_blank" : "",
    onClick: h,
    children: [
      c && (
        <H>
          {
            <div className="absolute inset-0">
              {
                <img
                  src={imageUrl}
                  alt={`image of ${title}`}
                  className="h-full w-full border-b border-black/10 object-cover"
                />
              }
            </div>
          }
        </H>
      ),
      <div className="flex flex-1 flex-col justify-between gap-1.5 p-3">
        <Z$2 $clamp={(mini !== undefined && mini) || c}>{title}</Z$2>
        <div className="flex items-center gap-1">
          {logoUrl ? (
            <R.Z url={logoUrl} name={t} size={13} />
          ) : (
            <U.Z url={url} size={13} />
          )}
          <div className="text-[10px] leading-3 text-gray-500 line-clamp-1">
            {t}
          </div>
        </div>
      </div>,
    ],
  });
}

While I haven't looked deeply into it, I suspect that perhaps this part that conditionally chooses which tag to use is possibly confusing the unminify?

jsxs(url ? "a" : "div", {

unminify CLI does not appy `un-esm` correctly

Currently, the unpacker will generate module mapping and the result of module scanning. But this information is not been passed to the unminify CLI because they were done in the unpacking process.

Ideally, module scanning should be moved into unminify. Not sure where the module mapping should go, maybe generate module-mapping.json?

`[un-jsx]` children from attribute and arguments are both present

Just tried out the CLI on a webpack'd chunk (Ref), and got a bunch of errors/warnings/similar:

[un-jsx] children from attribute and arguments are both present

You can see the full details/output including the snippets associated with those messages in the expandable below:

Details

First I unpacked it:

β‡’ cd ./unpacked/_next/static/chunks

β‡’ npx @wakaru/unpacker 496.js -o ./496-unpacked

Then I unminified it:

β‡’ npx @wakaru/unminify ./496-unpacked/* -o ./496-unminified
β€’ Transforming 496-unminified/496-unpacked/module-10604.js (882.1ms)
β€’ Transforming 496-unminified/496-unpacked/module-13282.js (391.9ms)
β€’ Transforming 496-unminified/496-unpacked/module-17915.js (1,451.5ms)
β€’ Transforming 496-unminified/496-unpacked/module-180.js (140.1ms)
β€’ Transforming 496-unminified/496-unpacked/module-19051.js (129.1ms)
β€’ Transforming 496-unminified/496-unpacked/module-21437.js (749.4ms)
β€’ Transforming 496-unminified/496-unpacked/module-2368.js (470.8ms)
β€’ Transforming 496-unminified/496-unpacked/module-24148.js (175.7ms)
β€’ Transforming 496-unminified/496-unpacked/module-25094.js (997.9ms)
β€’ Transforming 496-unminified/496-unpacked/module-25345.js (556.9ms)
β€’ Transforming 496-unminified/496-unpacked/module-25422.js (366.2ms)
β€’ Transforming 496-unminified/496-unpacked/module-30931.js (799.7ms)
β€’ Transforming 496-unminified/496-unpacked/module-31721.js (438.1ms)
β€’ Transforming 496-unminified/496-unpacked/module-32165.js (473.8ms)
β€’ Transforming 496-unminified/496-unpacked/module-32689.js (391.1ms)
β€’ Transforming 496-unminified/496-unpacked/module-36112.js (430.3ms)
β€’ Transforming 496-unminified/496-unpacked/module-36716.js (879.8ms)
β€’ Transforming 496-unminified/496-unpacked/module-37541.js (100.3ms)
β€’ Transforming 496-unminified/496-unpacked/module-38631.js (145.1ms)
β€’ Transforming 496-unminified/496-unpacked/module-44925.js (66.9ms)
β€’ Transforming 496-unminified/496-unpacked/module-45036.js (25,040.5ms)
β€’ Transforming 496-unminified/496-unpacked/module-46110.js (805.1ms)
[un-jsx] children from attribute and arguments are both present: jsx(g.Z, {
  isOpen: true,
  onClose: y,
  type: "success",
  children: <pre className="max-h-[80vh] overflow-auto whitespace-pre-wrap text-xs">{JSON.stringify(m[v], null, 2)}</pre>,
}, `DebugMessageModal-${v}`)
[un-jsx] children from attribute and arguments are both present: jsxs(N, {
  role: "button",
  onClick() {
    return x(t);
  },
  children: [
    <div className="text-xs font-medium uppercase text-gray-400">{role}{name && name !== role && ` (${name})`}{" -> "}{Ej(e)}</div>,
    <div>{RR(e)}</div>,
  ],
}, e.id)
β€’ Transforming 496-unminified/496-unpacked/module-48101.js (1,374.3ms)
[un-jsx] children from attribute and arguments are both present: jsx("div", { className: "text-yellow-500", children: e }, t)
[un-jsx] children from attribute and arguments are both present: jsx("div", { className: "text-red-500", children: e }, t)
β€’ Transforming 496-unminified/496-unpacked/module-49910.js (920.5ms)
β€’ Transforming 496-unminified/496-unpacked/module-5046.js (282.2ms)
β€’ Transforming 496-unminified/496-unpacked/module-56244.js (797.6ms)
β€’ Transforming 496-unminified/496-unpacked/module-57311.js (3,202.7ms)
β€’ Transforming 496-unminified/496-unpacked/module-57924.js (369.8ms)
β€’ Transforming 496-unminified/496-unpacked/module-61119.js (851.2ms)
β€’ Transforming 496-unminified/496-unpacked/module-62440.js (144ms)
[un-jsx] children from attribute and arguments are both present: jsx(w.Z, {
  clientThreadId: clientThreadId,
  messageId: id,
  size: _,
  className: Z$0(
    flag !== "danger" && isCompletionInProgress && "result-streaming",
    flag === "danger" && "text-red-500",
    flag === "warning" && "text-orange-500"
  ),
  children: e === "" ? "&#8203;" : Qd(e, citations),
}, t)
[un-jsx] children from attribute and arguments are both present: jsx("div", {
  className: "empty:hidden",
  children: flag === "danger" && F ? null : e,
}, t)
[un-jsx] children from attribute and arguments are both present: jsx(Et, { x: i, children: e }, t)
β€’ Transforming 496-unminified/496-unpacked/module-63390.js (5,541.5ms)
[un-jsx] children from attribute and arguments are both present: jsx(m.E.div, {
  className: "flex items-center gap-1",
  initial: { opacity: 0 },
  animate: {
    opacity: 1,
    transition: {
      duration: 0.2,
      ease: "easeIn",
    },
  },
  exit: {
    opacity: 0,
    transition: {
      duration: 0.2,
      ease: "easeIn",
    },
  },
  children: F
    ? <><y.Z {..._$0({}, A.showTips)} /><S.ZP size="xsmall" icon={v.rDJ} /></>
    : <><y.Z {..._$0({}, A.hideTips)} /><S.ZP size="xsmall" icon={v.rzC} /></>,
}, "show-hide")
[un-jsx] children from attribute and arguments are both present: jsxs(N.Z, {
  isOpen: z,
  onClose: eb,
  type: "success",
  size: "custom",
  className: "max-w-lg xl:max-w-xl",
  title: v.formatMessage(A.profileTitle),
  closeButton: <F.u
    sideOffset={4}
    interactive
    delayDuration={0}
    label={<div>{<y.Z
        {..._$1(_$0({}, A.profileSubhead), {
          values: {
            article(e) {
              return <a href={R} target="_blank" className="underline" rel="noreferrer">{e}</a>;
            },
          },
        })} />}</div>}
    side="bottom">{<S.HV className="h-6 w-6 flex-shrink-0 text-gray-500" />}</F.u>,
  children: [
    U || isLoading
      ? <div className="flex h-14 items-center justify-center">{<Z.Z />}</div>
      : <><p className="text-muted pb-3 pt-2 text-sm text-gray-600">{<y.Z {..._$0({}, A.aboutYouHelpText)} />}</p><J
        className="mb-3"
        onSubmit={ew}
        tip={<H>{<ul className="list-disc pl-5"><li>{<y.Z {..._$0({}, A.aboutUserTip1)} />}</li><li>{<y.Z {..._$0({}, A.aboutUserTip2)} />}</li><li>{<y.Z {..._$0({}, A.aboutUserTip3)} />}</li><li>{<y.Z {..._$0({}, A.aboutUserTip4)} />}</li><li>{<y.Z {..._$0({}, A.aboutUserTip5)} />}</li></ul>}</H>}
        hasError={el === "about_user_message"}
        value={aboutUserMessage$0 != null ? aboutUserMessage$0 : K}
        onChange={e => {
          return ea(t => ({
            aboutModelMessage:
              g ?? et,

            aboutUserMessage: e.target.value
          }));
        }} /><p className="text-muted py-3 text-sm text-gray-600">{<y.Z {..._$0({}, A.modelHelpText)} />}</p><J
        onSubmit={ew}
        tip={<H>{<ul className="list-disc pl-5"><li>{<y.Z {..._$0({}, A.modelTip1)} />}</li><li>{<y.Z {..._$0({}, A.modelTip2)} />}</li><li>{<y.Z {..._$0({}, A.modelTip3)} />}</li><li>{<y.Z {..._$0({}, A.modelTip4)} />}</li></ul>}</H>}
        hasError={el === "about_model_message"}
        value={aboutModelMessage$0 != null ? aboutModelMessage$0 : et}
        onChange={e => {
          return ea(t => ({
            aboutUserMessage:
              m ?? K,

            aboutModelMessage: e.target.value
          }));
        }} /></>,
    <div className="mt-5 sm:mt-4">{<div
        className="flex flex-grow flex-col items-stretch justify-between gap-0 sm:flex-row sm:items-center sm:gap-3"><div
          className="visible mt-5 text-center text-xs text-orange-400 empty:mt-0 sm:mt-4 sm:text-left ">{el &&
          <y.Z
            {..._$1(_$0({}, A.modApiVoilation), {
              values: {
                policyLink(e) {
                  return (
                    <a
                      href="https://platform.openai.com/docs/usage-policies/content-policy"
                      className="underline"
                      target="_blank"
                      rel="noreferrer">{e}</a>
                  );
                },
                feedbackLink(e) {
                  return (
                    <a
                      href="https://forms.gle/3gyAMj5r5rTEcgbs5"
                      className="underline"
                      target="_blank"
                      rel="noreferrer">{e}</a>
                  );
                },
              },
            })} />}</div><T.ZP.Actions
          secondaryButton={<T.ZP.Button onClick={eb}>{<y.Z {..._$0({}, A.cancel)} />}</T.ZP.Button>}
          primaryButton={<T.ZP.Button
            loading={isLoading$0}
            onClick={ew}
            color="primary"
            visuallyDisabled={ev}
            disabled={!ex}>{<y.Z {..._$0({}, A.save)} />}</T.ZP.Button>} /></div>}</div>,
  ],
}, "user-context")
[un-jsx] children from attribute and arguments are both present: jsx(N.Z, {
  isOpen: true,
  onClose: ej,
  type: "success",
  title: v.formatMessage(A.confirmCloseTitle),
  primaryButton: <T.ZP.Button
    title={v.formatMessage(A.confirmCloseOk)}
    color="danger"
    onClick={() => {
      ed();
      ej();
    }} />,
  secondaryButton: <T.ZP.Button
    title={v.formatMessage(A.confirmCloseCancel)}
    color="neutral"
    onClick={ej} />,
  children: <div className="text-sm">{<y.Z {..._$0({}, A.confirmCloseBody)} />}</div>,
}, "confirm-close")
β€’ Transforming 496-unminified/496-unpacked/module-63727.js (4,484.6ms)
β€’ Transforming 496-unminified/496-unpacked/module-66523.js (476.3ms)
β€’ Transforming 496-unminified/496-unpacked/module-69403.js (327.1ms)
β€’ Transforming 496-unminified/496-unpacked/module-697.js (318.5ms)
β€’ Transforming 496-unminified/496-unpacked/module-74437.js (249.3ms)
β€’ Transforming 496-unminified/496-unpacked/module-75179.js (276.4ms)
β€’ Transforming 496-unminified/496-unpacked/module-75515.js (153.4ms)
β€’ Transforming 496-unminified/496-unpacked/module-75527.js (4,659.6ms)
β€’ Transforming 496-unminified/496-unpacked/module-76559.js (452.7ms)
β€’ Transforming 496-unminified/496-unpacked/module-77442.js (400.8ms)
β€’ Transforming 496-unminified/496-unpacked/module-85449.js (406.7ms)
β€’ Transforming 496-unminified/496-unpacked/module-86433.js (596.6ms)
β€’ Transforming 496-unminified/496-unpacked/module-86526.js (103.7ms)
β€’ Transforming 496-unminified/496-unpacked/module-86573.js (1,530.1ms)
β€’ Transforming 496-unminified/496-unpacked/module-870.js (215ms)
β€’ Transforming 496-unminified/496-unpacked/module-87105.js (6,171.4ms)
β€’ Transforming 496-unminified/496-unpacked/module-87316.js (163.9ms)
β€’ Transforming 496-unminified/496-unpacked/module-90076.js (1,444.5ms)
β€’ Transforming 496-unminified/496-unpacked/module-94860.js (499.8ms)
β€’ Transforming 496-unminified/496-unpacked/module-97732.js (1,424.5ms)

Is this an error, or a warning, or? Is there anything we need to do about it, or that should be improved in wakaru to handle this case better?

support `un-mangle` identifiers

using this code:

const c=n.a.get("API.lemonade.url"),d=n.a.get("API.lemonade.urlLinear"),u=n.a.get("API.lemonade.urlVod"),p=n.a.get("API.lemonade.platform"),m=n.a.get("API.lemonade.timeout");

I get this result:

const c = n.a.get("API.lemonade.url");
const d = n.a.get("API.lemonade.urlLinear");
const u = n.a.get("API.lemonade.urlVod");
const p = n.a.get("API.lemonade.platform");
const m = n.a.get("API.lemonade.timeout");

using another tool, I get:

const const1_ = n.a.get("API.lemonade.url"),
  const2_ = n.a.get("API.lemonade.urlLinear"),
  const3_ = n.a.get("API.lemonade.urlVod"),
  const4_ = n.a.get("API.lemonade.platform"),
  const5_ = /* global */ n.a.get("API.lemonade.timeout");

https://richsnapp.com/tools/code-unmangler

using another tool, I get:

const varFastenedWheel = n.a.get('API.lemonade.url'), varHairStepped = n.a.get('API.lemonade.urlLinear'), varFourRising = n.a.get('API.lemonade.urlVod'), varCourageYouth = n.a.get('API.lemonade.platform'), varBellRain = n.a.get('API.lemonade.timeout');

https://github.com/relative/synchrony

[module-detection] radix-ui/primitives

This relates to the 'module-detection' feature described in the following issue:

Overview

Code

Unminifying this source (Ref), module-15838.js/etc:

Note: Not including the unminified examples here currently as the file is quite large

Searching for some of the symbols from module-15838.js on GitHub code search:

Of those results, these look the most promising:


Using the madge CLI/lib that I mentioned in another issue (Ref), I was able to automatically find/generate a graph of all of the module dependencies related to this.

I narrowed down on this by first creating a larger image of all the dependencies in a chunk/etc, finding something that looked like a lib like this, then running it again on that module.

Note also that I haven't manually checked through all of the modules mentioned here yet.

.madgerc
// See: https://github.com/pahen/madge#configuration
{
  "fileExtensions": ["js"],
  "detectiveOptions": {
    "es6": {
      "mixedImports": true,
      "skipTypeImports": true
    },
    "ts": {
      "mixedImports": true,
      "skipTypeImports": true
    }
  }
}
β‡’ npx madge --image 15838.svg stage3-unminified/pages/_app/module-15838.js

15838

(blue has dependencies, green doesn't)

madge --json output
β‡’ npm run-script madge:json stage3-unminified/pages/_app/module-15838.js

> [email protected] madge:json
> f() { MODULE_PATH="$1"; npx madge --json "$MODULE_PATH"; }; f stage3-unminified/pages/_app/module-15838.js

{
  "module-15838.js": [
    "module-36646.js",
    "module-37812.js",
    "module-45675.js",
    "module-49270.js",
    "module-63543.js",
    "module-70007.js",
    "module-72901.js",
    "module-74039.js",
    "module-7824.js",
    "module-86004.js",
    "module-88817.js",
    "module-9235.js",
    "module-94251.js"
  ],
  "module-36646.js": [],
  "module-37812.js": [
    "module-45675.js",
    "module-7824.js"
  ],
  "module-45675.js": [],
  "module-49270.js": [],
  "module-63543.js": [
    "module-36646.js",
    "module-45675.js",
    "module-49270.js",
    "module-7824.js",
    "module-79388.js",
    "module-82094.js",
    "module-9137.js"
  ],
  "module-70007.js": [
    "module-45675.js",
    "module-7824.js"
  ],
  "module-72901.js": [],
  "module-74039.js": [
    "module-45675.js",
    "module-49270.js",
    "module-72901.js",
    "module-7824.js",
    "module-9137.js"
  ],
  "module-7824.js": [
    "module-45675.js",
    "module-94251.js"
  ],
  "module-79388.js": [],
  "module-82094.js": [
    "module-79388.js"
  ],
  "module-86004.js": [
    "module-9137.js"
  ],
  "module-88817.js": [
    "module-79388.js"
  ],
  "module-9137.js": [],
  "module-9235.js": [
    "module-49270.js",
    "module-79388.js"
  ],
  "module-94251.js": [
    "module-45675.js",
    "module-49270.js"
  ]
}

CLI bug: Interactive mode -> Unpacker -> Input file path -> 'Input does not exist' when using `prefix/**/*.js` glob pattern

Trying the new CLI in interactive mode, it seems to have issues when I give it an input file path like unpacked/**/*.js, even though that path definitely exists (running from the root dir of this checked out repo (Ref))

β‡’ npx @wakaru/cli

β”Œ   Wakaru CLI v0.0.2
β”‚
β”‚  Run "wakaru --help" for usage options
β”‚
β—‡  Select features to use (Use <space> to select, <enter> to submit)
β”‚  Unpacker - Unpacks bundled code into separated modules
β”‚
β””  Selected features: Unpacker

β”Œ   Unpacker
β”‚
β–²  Input file path (Supports glob patterns)
β”‚  unpacked/**/*.js_
β””  Input does not exist

If I change the input path to something like unpacked/, then the error message changes to Input is not a file.

While I haven't looked too deeply into it, I think there might currently be a check to ensure that the provided input is a file; but that would then break the ability to import folders and globs.


While it's OOS of this issue, I also noticed that it's not possible to tab complete files/paths while using the interactive mode. That may be by design, but it would probably make it more user friendly. I can open this as a separate issue if desired.

Edit: See:

CLI: Allow tab-completing path suggestions while using interactive mode

Originally raised here:

While it's OOS of this issue, I also noticed that it's not possible to tab complete files/paths while using the interactive mode. That may be by design, but it would probably make it more user friendly. I can open this as a separate issue if desired.

Originally posted by @0xdevalias in #81 (comment)

I also noticed that it's not possible to tab complete files/paths while using the interactive mode. That may be by design, but it would probably make it more user friendly. I can open this as a separate issue if desired.

This one is on my todo list. Can open in another one.

Originally posted by @pionxzh in #81 (comment)

TypeError: Cannot read properties of undefined (reading 'line') at Object.fixFaultyLocations (node_modules/recast/lib/util.js:153:23)

Attempting to unpack/unminify some webpacked code (Ref):

β‡’ cd ./unpacked/_next/static/chunks

β‡’ npx @wakaru/unpacker pages/_app.js -o ./pages/_app-unpacked/
Generated 213 modules from pages/_app.js to pages/_app-unpacked (24,049ms)

β‡’ npx @wakaru/unminify ./pages/_app-unpacked/* -o ./pages/_app-unminified
# ..snip..

I got the following error while attempting to unminify:

# ..snip..
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-76984.js (203.7ms)
TypeError: Cannot read properties of undefined (reading 'line')
    at Object.fixFaultyLocations (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/util.js:153:23)
    at TreeCopier.TCp.copy (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/parser.js:138:10)
    at TreeCopier.TCp.copy (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/parser.js:190:30)
    at TreeCopier.TCp.copy (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/parser.js:190:30)
    at TreeCopier.TCp.copy (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/parser.js:190:30)
    at TreeCopier.TCp.copy (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/parser.js:190:30)
    at TreeCopier.TCp.copy (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/parser.js:190:30)
    at TreeCopier.TCp.copy (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/parser.js:190:30)
    at TreeCopier.<anonymous> (/Users/devalias/dev/0xdevalias/REDACTED/node_modules/recast/lib/parser.js:131:30)
    at Array.forEach (<anonymous>)
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-7718.js (2,651.5ms)
# ..snip..

Multiple exports of "X" found, only the last one will be kept

Attempting to unpack/unminify some webpacked code (Ref):

β‡’ cd ./unpacked/_next/static/chunks

β‡’ npx @wakaru/unpacker pages/_app.js -o ./pages/_app-unpacked/
Generated 213 modules from pages/_app.js to pages/_app-unpacked (24,049ms)

β‡’ npx @wakaru/unminify ./pages/_app-unpacked/* -o ./pages/_app-unminified
# ..snip..

I got a bunch of warnings(?) similar to the following while attempting to unminify; though it's not clear if these warnings relate to the module listed before them, or after them:

β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-10538.js (361.8ms)
Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-10608.js (64.1ms)

.. snip..

β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-138.js (153.3ms)
Multiple exports of "getUserCacheKey" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-13957.js (290.8ms)

etc

The 'multiple exports' log seems to happen in unminify's replaceWithExportDeclaration:

if (exportsMap.has(name)) {
const previousPath = exportsMap.get(name)!
previousPath.prune()
exportsMap.delete(name)
console.warn(`Multiple exports of "${name}" found, only the last one will be kept`)
// TODO: handle multiple exports
}

The 'Transforming' log seems to happen in unminify's CLI, and appears after the call to runDefaultTransformation (which calls runTransformations, which uses transformationMap, which includes unEsm, which contains transformAST, which calls transformExport, which calls replaceWithExportDeclaration)

This implies that the warning before the module applies to that module..

Full list of the different warnings + modules that caused them
Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-10608.js (64.1ms)

Multiple exports of "getUserCacheKey" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-13957.js (290.8ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-16861.js (163.4ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-18458.js (212.8ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-21121.js (147.7ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-21508.js (55.2ms)

Multiple exports of "Base64" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-27046.js (283.1ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-36386.js (97.5ms)

Multiple exports of "checkCookies" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-44544.js (892.6ms)

Multiple exports of "STORAGE_PREFIX" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-49787.js (94.5ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-50201.js (2,190.6ms)

Multiple exports of "ExceptionEndpoint" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-5421.js (1,131.6ms)

Multiple exports of "StatsigInvalidArgumentError" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-5502.js (238.5ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-60925.js (160.4ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-65860.js (93.2ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-6631.js (202.8ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-67523.js (105.4ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-67860.js (75.8ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-69270.js (89.2ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-70479.js (123.6ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-71083.js (576.5ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-74861.js (51.2ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-76226.js (123.2ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-77415.js (107.2ms)

Multiple exports of "UnsupportedStrategy" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-80456.js (2,691.6ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-84019.js (130.3ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-8629.js (83.7ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-91921.js (75ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-94355.js (54.4ms)

Multiple exports of "default" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-98421.js (65.1ms)

Multiple exports of "difference" found, only the last one will be kept
β€’ Transforming pages/_app-unminified/pages/_app-unpacked/module-98480.js (80.3ms)

I haven't yet looked through the individual modules that caused warnings, but I wanted to open this issue as a placeholder for adding any more info about them that seems relevant as I do.

explore 'AST fingerprinting' for module/function identification (eg. to assist smart / stable renames, etc)

Another area I started looking into (but haven't deeply explored yet) for both figuring out how to map variable names to sections of code in a 'smart' way, and potentially also for module identification (see #41); is in the space of 'structural AST fingerprinting' or 'code similarity' algorithms and similar. (I realise that this is a rather deep/esoteric angle to be looking at this from, and that there are likely going to be far simpler/easier ways to implement the variable mapping/module identification in a 'good enough' way without going to this level of depth; but I'm curious to explore it regardless, to see if any good ideas come out of it)

I haven't gotten too far in my reading yet (got distracted on other things), but the high level of my idea was that maybe we could generate an 'AST fingerprint' that isn't impacted by the variable/function/etc names ('symbols') changing during minification; and then use that as the basis for the 'key' in the 'mappings file'; as that fingerprint could theoretically still identify a 'scope' (which might be a literal JS scope, or might be a higher level abstraction that we decide makes sense; the most abstract being probably at the bundled module level) even if the bundler decides to move some functions around to a different module/etc. Then obviously if we were able to generate those 'resilient fingerprints' to identify code even when it's been minified, that would make perfect sense to apply to module detection/etc (see #41) as well.

Some of the high level ideas / search terms that I was using to start my research in that area was things like:

  • AST fingerprinting
  • Source code similarity fingerprinting
  • Control flow graphs
  • Call flow graphs
  • Program dependence graph
  • etc

Here is a link dump of a bunch of the tabs I have open but haven't got around to reviewing in depth yet, RE: 'AST fingerprinting' / Code Similarity / etc:

Unsorted/Unreviewed Initial Link Dump RE: 'AST fingerprinting' / Code Similarity
  • https://en.wikipedia.org/wiki/Program_dependence_graph
    • Program Dependence Graph - Wikipedia

    • In computer science, a Program Dependence Graph (PDG) is a representation of a program's control and data dependencies. It's a directed graph where nodes represent program statements, and edges represent dependencies between these statements. PDGs are useful in various program analysis tasks, including optimizations, debugging, and understanding program behavior.

  • https://en.wikipedia.org/wiki/Control-flow_graph
    • Control-Flow Graph - Wikipedia

    • In computer science, a control-flow graph (CFG) is a representation, using graph notation, of all paths that might be traversed through a program during its execution.

    • In a control-flow graph each node in the graph represents a basic block, i.e. a straight-line piece of code without any jumps or jump targets; jump targets start a block, and jumps end a block. Directed edges are used to represent jumps in the control flow. There are, in most presentations, two specially designated blocks: the entry block, through which control enters into the flow graph, and the exit block, through which all control flow leaves.

  • https://stackoverflow.com/questions/7283702/assembly-level-function-fingerprint
    • Stack Overflow: Assembly-level function fingerprint (2011)

  • https://stackoverflow.com/questions/15087195/data-flow-graph-construction
    • Stack Overflow: Data Flow Graph Construction (2013)

  • https://codereview.stackexchange.com/questions/276387/call-flow-graph-from-python-abstract-syntax-tree
    • Code Review Stack Exchange: Call-flow graph from Python abstract syntax tree (2022)

  • https://codeql.github.com/docs/writing-codeql-queries/about-data-flow-analysis/
    • CodeQL Documentation: About data flow analysis

    • Data flow analysis is used to compute the possible values that a variable can hold at various points in a program, determining how those values propagate through the program and where they are used.

  • https://clang.llvm.org/docs/DataFlowAnalysisIntro.html
    • Clang Documentation: Data flow analysis: an informal introduction

    • This document introduces data flow analysis in an informal way. The goal is to give the reader an intuitive understanding of how it works, and show how it applies to a range of refactoring and bug finding problems.

    • Data flow analysis is a static analysis technique that proves facts about a program or its fragment. It can make conclusions about all paths through the program, while taking control flow into account and scaling to large programs. The basic idea is propagating facts about the program through the edges of the control flow graph (CFG) until a fixpoint is reached.

  • https://openreview.net/forum?id=BJxWx0NYPr
    • Adaptive Structural Fingerprints for Graph Attention Networks (2019)

    • Graph attention network (GAT) is a promising framework to perform convolution and massage passing on graphs. Yet, how to fully exploit rich structural information in the attention mechanism remains a challenge. In the current version, GAT calculates attention scores mainly using node features and among one-hop neighbors, while increasing the attention range to higher-order neighbors can negatively affect its performance, reflecting the over-smoothing risk of GAT (or graph neural networks in general), and the ineffectiveness in exploiting graph structural details. In this paper, we propose an "adaptive structural fingerprint" (ADSF) model to fully exploit graph topological details in graph attention network. The key idea is to contextualize each node with a weighted, learnable receptive field encoding rich and diverse local graph structures. By doing this, structural interactions between the nodes can be inferred accurately, thus significantly improving subsequent attention layer as well as the convergence of learning. Furthermore, our model provides a useful platform for different subspaces of node features and various scales of graph structures to 'cross-talk' with each other through the learning of multi-head attention, being particularly useful in handling complex real-world data. Empirical results demonstrate the power of our approach in exploiting rich structural information in GAT and in alleviating the intrinsic oversmoothing problem in graph neural networks.

  • https://dl.acm.org/doi/10.1145/3486860
    • A Survey of Binary Code Fingerprinting Approaches: Taxonomy, Methodologies, and Features (2022)

    • Binary code fingerprinting is crucial in many security applications. Examples include malware detection, software infringement, vulnerability analysis, and digital forensics. It is also useful for security researchers and reverse engineers since it enables high fidelity reasoning about the binary code such as revealing the functionality, authorship, libraries used, and vulnerabilities. Numerous studies have investigated binary code with the goal of extracting fingerprints that can illuminate the semantics of a target application. However, extracting fingerprints is a challenging task since a substantial amount of significant information will be lost during compilation, notably, variable and function naming, the original data and control flow structures, comments, semantic information, and the code layout. This article provides the first systematic review of existing binary code fingerprinting approaches and the contexts in which they are used. In addition, it discusses the applications that rely on binary code fingerprints, the information that can be captured during the fingerprinting process, and the approaches used and their implementations. It also addresses limitations and open questions related to the fingerprinting process and proposes future directions.

  • https://inria.hal.science/hal-01648996/document
    • BinSign: Fingerprinting Binary Functions to Support Automated Analysis of Code Executables (2017)

    • Binary code fingerprinting is a challenging problem that requires an in-depth analysis of binary components for deriving identifiable signatures. Fingerprints are useful in automating reverse engineering tasks including clone detection, library identification, authorship attribution, cyber forensics, patch analysis, malware clustering, binary auditing, etc. In this paper, we present BinSign, a binary function fingerprinting framework. The main objective of BinSign is providing an accurate and scalable solution to binary code fingerprinting by computing and matching structural and syntactic code profiles for disassemblies. We describe our methodology and evaluate its performance in several use cases, including function reuse, malware analysis, and indexing scalability. Additionally, we emphasize the scalability aspect of BinSign. We perform experiments on a database of 6 million functions. The indexing process requires an average time of 0.0072 seconds per function. We find that BinSign achieves higher accuracy compared to existing tools.

  • https://hal.science/hal-00627811/document
    • Syntax tree fingerprinting: a foundation for source code similarity detection (2011)

    • Plagiarism detection and clone refactoring in software depend on one common concern: finding similar source chunks across large repositories. However, since code duplication in software is often the result of copy-paste behaviors, only minor modifications are expected between shared codes. On the contrary, in a plagiarism detection context, edits are more extensive and exact matching strategies show their limits.
      Among the three main representations used by source code similarity detection tools, namely the linear token sequences, the Abstract Syntax Tree (AST) and the Program Dependency Graph (PDG), we believe that the AST could efficiently support the program analysis and transformations required for the advanced similarity detection process.
      In this paper we present a simple and scalable architecture based on syntax tree fingerprinting. Thanks to a study of several hashing strategies reducing false-positive collisions, we propose a framework that efficiently indexes AST representations in a database, that quickly detects exact (w.r.t source code abstraction) clone clusters and that easily retrieves their corresponding ASTs. Our aim is to allow further processing of neighboring exact matches in order to identify the larger approximate matches, dealing with the common modification patterns seen in the intra-project copy-pastes and in the plagiarism cases.

  • https://ieeexplore.ieee.org/document/5090050
    • Syntax tree fingerprinting for source code similarity detection (2009)

    • Numerous approaches based on metrics, token sequence pattern-matching, abstract syntax tree (AST) or program dependency graph (PDG) analysis have already been proposed to highlight similarities in source code: in this paper we present a simple and scalable architecture based on AST fingerprinting. Thanks to a study of several hashing strategies reducing false-positive collisions, we propose a framework that efficiently indexes AST representations in a database, that quickly detects exact (w.r.t source code abstraction) clone clusters and that easily retrieves their corresponding ASTs. Our aim is to allow further processing of neighboring exact matches in order to identify the larger approximate matches, dealing with the common modification patterns seen in the intra-project copy-pastes and in the plagiarism cases.

    • https://igm.univ-mlv.fr/~chilowi/research/syntax_tree_fingerprinting/syntax_tree_fingerprinting_ICPC09.pdf
  • https://ieeexplore.ieee.org/document/9960266
    • Source Code Plagiarism Detection Based on Abstract Syntax Tree Fingerprintings (2022)

    • Syntax Tree (AST) is an abstract logical structure of source code represented as a tree. This research utilizes information of fingerprinting with AST to locate the similarities between source codes. The proposed method can detect plagiarism in source codes using the number of duplicated logical structures. The structural information of program is stored in the fingerprints format. Then, the fingerprints of source codes are compared to identify number of similar nodes. The final output is calculated from number of similar nodes known as similarities scores. The result shows that the proposed method accurately captures the common modification techniques from basic to advance.

  • https://digitalcommons.calpoly.edu/theses/2040/
    • Cloneless: Code Clone Detection via Program Dependence Graphs with Relaxed Constraints (2019)

    • Code clones are pieces of code that have the same functionality. While some clones may structurally match one another, others may look drastically different. The inclusion of code clones clutters a code base, leading to increased costs through maintenance. Duplicate code is introduced through a variety of means, such as copy-pasting, code generated by tools, or developers unintentionally writing similar pieces of code. While manual clone identification may be more accurate than automated detection, it is infeasible due to the extensive size of many code bases. Software code clone detection methods have differing degree of success based on the analysis performed. This thesis outlines a method of detecting clones using a program dependence graph and subgraph isomorphism to identify similar subgraphs, ultimately illuminating clones. The project imposes few constraints when comparing code segments to potentially reveal more clones.

    • https://digitalcommons.calpoly.edu/cgi/viewcontent.cgi?article=3437&context=theses
  • https://dl.acm.org/doi/10.1145/1286821.1286826
    • Dynamic graph-based software fingerprinting (2007)

    • Fingerprinting embeds a secret message into a cover message. In media fingerprinting, the secret is usually a copyright notice and the cover a digital image. Fingerprinting an object discourages intellectual property theft, or when such theft has occurred, allows us to prove ownership.

      The Software Fingerprinting problem can be described as follows. Embed a structure W into a program P such that: W can be reliably located and extracted from P even after P has been subjected to code transformations such as translation, optimization and obfuscation; W is stealthy; W has a high data rate; embedding W into P does not adversely affect the performance of P; and W has a mathematical property that allows us to argue that its presence in P is the result of deliberate actions.

      In this article, we describe a software fingerprinting technique in which a dynamic graph fingerprint is stored in the execution state of a program. Because of the hardness of pointer alias analysis such fingerprints are difficult to attack automatically.

    • https://dl.acm.org/doi/pdf/10.1145/1286821.1286826
  • https://patents.google.com/patent/US9459861B1/en
    • Systems and methods for detecting copied computer code using fingerprints (2016)

    • Systems and methods of detecting copying of computer code or portions of computer code involve generating unique fingerprints from compiled computer binaries. The unique fingerprints are simplified representations of functions in the compiled computer binaries and are compared with each other to identify similarities between functions in the respective compiled computer binaries. Copying can be detected when there are sufficient similarities between fingerprints of two functions.

  • https://www.unomaha.edu/college-of-information-science-and-technology/research-labs/_files/software-nsf.pdf
    • Software Fingerprinting in LLVM (2021)

    • Executable steganography, the hiding of software machine code inside of a larger program, is a potential approach to introduce new software protection constructs such as watermarks or fingerprints. Software fingerprinting is, therefore, a process similar to steganography, hiding data within other data. The goal of fingerprinting is to hide a unique secret message, such as a serial number, into copies of an executable program in order to provide proof of ownership of that program. Fingerprints are a special case of watermarks, with the difference being that each fingerprint is unique to each copy of a program. Traditionally, researchers describe four aims that a software fingerprint should achieve. These include the fingerprint should be difficult to remove, it should not be obvious, it should have a low false positive rate, and it should have negligible impact on performance. In this research, we propose to extend these objectives and introduce a fifth aim: that software fingerprints should be machine independent. As a result, the same fingerprinting method can be used regardless of the architecture used to execute the program. Hence, this paper presents an approach towardsthe realization of machine-independent fingerprinting of executable programs. We make use of Low-Level Virtual Machine (LLVM) intermediate representation during the software compilation process to demonstrate both a simple static fingerprinting method as well as a dynamic method, which displays our aim of hardware independent fingerprinting. The research contribution includes a realization of the approach using the LLVM infrastructure and provides a proof of concept for both simple static and dynamic watermarks that are architecture neutral.

  • https://www.computer.org/csdl/journal/ts/2023/08/10125077/1Nc4Vd4vb7W
    • Graph-of-Code: Semantic Clone Detection Using Graph Fingerprints (2023)

    • The code clone detection issue has been researched using a number of explicit factors based on the tokens and contents and found effective results. However, exposing code contents may be an impractical option because of privacy and security factors. Moreover, the lack of scalability of past methods is an important challenge. The code flow states can be inferred by code structure and implicitly represented using empirical graphs. The assumption is that modelling of the code clone detection problem can be achieved without the content of the codes being revealed. Here, a Graph-of-Code concept for the code clone detection problem is introduced, which represents codes into graphs. While Graph-of-Code provides structural properties and quantification of its characteristics, it can exclude code contents or tokens to identify the clone type. The aim is to evaluate the impact of graph-of-code structural properties on the performance of code clone detection. This work employs a feature extraction-based approach for unlabelled graphs. The approach generates a β€œGraph Fingerprint” which represents different topological feature levels. The results of code clone detection indicate that code structure has a significant role in detecting clone types. We found different GoC-models outperform others. The models achieve between 96% to 99% in detecting code clones based on recall, precision, and F1-Score. The GoC approach is capable in detecting code clones with scalable dataset and with preserving codes privacy.

  • https://www.cs.columbia.edu/~suman/secure_sw_devel/Basic_Program_Analysis_CF.pdf
    • Slides: Basic Program Analysis - Suman Jana

    • ChatGPT Summary / Abstract:
      • Title: Basic Program Analysis

        Author: Suman Jana

        Institution: Columbia University

        Abstract:
        This document delves into the foundational concepts and techniques involved in program analysis, particularly focusing on control flow and data flow analysis essential for identifying security bugs in source code. The objective is to equip readers with the understanding and tools needed to effectively analyze programs without building systems from scratch, utilizing existing frameworks such as LLVM for customization and enhancement of analysis processes.

        The core discussion includes an overview of compiler design with specific emphasis on the Abstract Syntax Tree (AST), Control Flow Graph (CFG), and Data Flow Analysis. These elements are critical in understanding the structure of source code and its execution flow. The document highlights the conversion of source code into AST and subsequently into CFG, where data flow analysis can be applied to optimize code and identify potential security vulnerabilities.

        Additionally, the paper explores more complex topics like identifying basic blocks within CFG, constructing CFG from basic blocks, and advanced concepts such as loop identification and the concept of dominators in control flow. It also addresses the challenges and solutions related to handling irreducible Control Flow Graphs (CFGs), which are crucial for the analysis of less structured code.

        Keywords: Program Analysis, Compiler Design, Abstract Syntax Tree (AST), Control Flow Graph (CFG), Data Flow Analysis, LLVM, Security Bugs.

  • https://www.researchgate.net/publication/370980383_A_graph-based_code_representation_method_to_improve_code_readability_classification
    • A graph-based code representation method to improve code readability classification (2023)

    • Context Code readability is crucial for developers since it is closely related to code maintenance and affects developers’ work efficiency. Code readability classification refers to the source code being classified as pre-defined certain levels according to its readability. So far, many code readability classification models have been proposed in existing studies, including deep learning networks that have achieved relatively high accuracy and good performance. Objective However, in terms of representation, these methods lack effective preservation of the syntactic and semantic structure of the source code. To extract these features, we propose a graph-based code representation method. Method Firstly, the source code is parsed into a graph containing its abstract syntax tree (AST) combined with control and data flow edges to reserve the semantic structural information and then we convert the graph nodes’ source code and type information into vectors. Finally, we train our graph neural networks model composing Graph Convolutional Network (GCN), DMoNPooling, and K-dimensional Graph Neural Networks (k-GNNs) layers to extract these features from the program graph. Result We evaluate our approach to the task of code readability classification using a Java dataset provided by Scalabrino et al. (2016). The results show that our method achieves 72.5% and 88% in three-class and two-class classification accuracy, respectively. Conclusion We are the first to introduce graph-based representation into code readability classification. Our method outperforms state-of-the-art readability models, which suggests that the graph-based code representation method is effective in extracting syntactic and semantic information from source code, and ultimately improves code readability classification.

Edit: Started a new gist to keep my notes/references altogether in one place in a better way + added the above linkdump to it:

Originally posted by @0xdevalias in #34 (comment)

See Also

[module-detection] statsig-io/js-client

This relates to the 'module-detection' feature described in the following issue:

Overview

  • https://github.com/statsig-io/js-client
    • Statsig
      Statsig JavaScript Client SDK

    • The JavaScript SDK for single user client environments.

    • Statsig helps you move faster with feature gates (feature flags), and/or dynamic configs. It also allows you to run A/B/n tests to validate your new features and understand their impact on your KPIs.

Code

Unminifying this source (Ref), module-56340.js:

Note: Not including the unminified examples here currently as the file is quite large

Searching for some of the symbols from module-56340.js on GitHub code search:

Of those results, this looks the most promising:


Using the madge CLI/lib that I mentioned in another issue (Ref), I was able to automatically find/generate a graph of all of the module dependencies related to this.

I narrowed down on this by first creating a larger image of all the dependencies in a chunk/etc, finding something that looked like a lib like this, then running it again on that module.

Note also that I haven't manually checked through all of the modules mentioned here yet.

.madgerc
// See: https://github.com/pahen/madge#configuration
{
  "fileExtensions": ["js"],
  "detectiveOptions": {
    "es6": {
      "mixedImports": true,
      "skipTypeImports": true
    },
    "ts": {
      "mixedImports": true,
      "skipTypeImports": true
    }
  }
}
β‡’ npx madge --image 56340.svg stage3-unminified/pages/_app/module-56340.js

56340

(blue has dependencies, green doesn't)

madge --json output
β‡’ npm run-script madge:json stage3-unminified/pages/_app/module-56340.js
> [email protected] madge:json
> f() { MODULE_PATH="$1"; npx madge --json "$MODULE_PATH"; }; f stage3-unminified/pages/_app/module-56340.js

{
  "module-12229.js": [],
  "module-13290.js": [
    "module-44675.js"
  ],
  "module-13957.js": [
    "module-13290.js",
    "module-27046.js"
  ],
  "module-25609.js": [],
  "module-27046.js": [],
  "module-3009.js": [
    "module-13957.js",
    "module-49787.js"
  ],
  "module-30227.js": [
    "module-13957.js",
    "module-5502.js",
    "module-98480.js"
  ],
  "module-44675.js": [],
  "module-49787.js": [],
  "module-51475.js": [],
  "module-5502.js": [],
  "module-56340.js": [
    "module-12229.js",
    "module-25609.js",
    "module-3009.js",
    "module-30227.js",
    "module-51475.js",
    "module-5502.js"
  ],
  "module-98480.js": []
}

CLI bug: Error: ENOENT: no such file or directory, open 'out/perf.json' (when using `--perf` alongside `--unpacker-output` / `--unminify-output`)

I got the error:

Error: ENOENT: no such file or directory, open 'out/perf.json'

When running the following command, on this code (Ref):

β‡’ CHUNK_NAME="framework"; npx @wakaru/cli all unpacked/_next/static/chunks/${CHUNK_NAME}.js --unpacker-output stage2-unpacked/${CHUNK_NAME} --unminify-output stage3-unminified/${CHUNK_NAME} --perf

β”Œ   Wakaru CLI v0.0.2
β”‚
β””  Selected features: Unpacker, Unminify

β”Œ   Unpacker
β”‚
β—‡  Unpacking...
β”‚
β—‡  Finished
β”‚
β—†  Successfully generated 9 modules (4.51s)
β”‚
β””  Output directory: ./stage2-unpacked/framework

β”Œ   Unminify
β”‚
β—‡  Unminifying... (concurrency: 1)
β”‚
β—‡  Finished
β”‚
β—†  Successfully unminified 9 files (2m21s)
β”‚
β””  Output directory: ./stage3-unminified/framework

..snip..

Error: ENOENT: no such file or directory, open 'out/perf.json'
    at Object.openSync (node:fs:592:3)
    at Object.writeFileSync (node:fs:2323:35)
    at Object.writeFileSync (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/jsonfile/index.js:78:13)
    at writePerfStats (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/cli.cjs:1676:21)
    at nonInteractive (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/cli.cjs:1653:7)
    at async Object.handler (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/cli.cjs:1301:5) {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: 'out/perf.json'
}
Full Output
β‡’ CHUNK_NAME="framework"; npx @wakaru/cli all unpacked/_next/static/chunks/${CHUNK_NAME}.js --unpacker-output stage2-unpacked/${CHUNK_NAME} --unminify-output stage3-unminified/${CHUNK_NAME} --perf

β”Œ   Wakaru CLI v0.0.2
β”‚
β””  Selected features: Unpacker, Unminify

β”Œ   Unpacker
β”‚
β—‡  Unpacking...
β”‚
β—‡  Finished
β”‚
β—†  Successfully generated 9 modules (4.51s)
β”‚
β””  Output directory: ./stage2-unpacked/framework

β”Œ   Unminify
β”‚
β—‡  Unminifying... (concurrency: 1)
β”‚
β—‡  Finished
β”‚
β—†  Successfully unminified 9 files (2m21s)
β”‚
β””  Output directory: ./stage3-unminified/framework


β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”
β”‚ (index) β”‚            key             β”‚ time β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€
β”‚    0    β”‚        'prettier-1'        β”‚ 692  β”‚
β”‚    1    β”‚         'prettier'         β”‚ 725  β”‚
β”‚    2    β”‚      'module-mapping'      β”‚ 1971 β”‚
β”‚    3    β”‚     'un-curly-braces'      β”‚ 2702 β”‚
β”‚    4    β”‚         'un-iife'          β”‚ 2805 β”‚
β”‚    5    β”‚  'un-assignment-merging'   β”‚ 2880 β”‚
β”‚    6    β”‚      'un-async-await'      β”‚ 2961 β”‚
β”‚    7    β”‚  'un-sequence-expression'  β”‚ 3047 β”‚
β”‚    8    β”‚       'un-infinity'        β”‚ 3063 β”‚
β”‚    9    β”‚   'un-variable-merging'    β”‚ 3071 β”‚
β”‚   10    β”‚        'un-return'         β”‚ 3112 β”‚
β”‚   11    β”‚        'un-boolean'        β”‚ 3118 β”‚
β”‚   12    β”‚   'un-template-literal'    β”‚ 3153 β”‚
β”‚   13    β”‚       'un-es6-class'       β”‚ 3164 β”‚
β”‚   14    β”‚     'un-esmodule-flag'     β”‚ 3218 β”‚
β”‚   15    β”‚          'un-jsx'          β”‚ 3228 β”‚
β”‚   16    β”‚      'un-use-strict'       β”‚ 3326 β”‚
β”‚   17    β”‚      'un-while-loop'       β”‚ 3394 β”‚
β”‚   18    β”‚       'un-undefined'       β”‚ 3489 β”‚
β”‚   19    β”‚   'un-type-constructor'    β”‚ 3527 β”‚
β”‚   20    β”‚         'un-enum'          β”‚ 3529 β”‚
β”‚   21    β”‚     'un-conditionals'      β”‚ 3532 β”‚
β”‚   22    β”‚  'un-nullish-coalescing'   β”‚ 3645 β”‚
β”‚   23    β”‚     'un-export-rename'     β”‚ 3682 β”‚
β”‚   24    β”‚        'un-typeof'         β”‚ 3689 β”‚
β”‚   25    β”‚   'un-builtin-prototype'   β”‚ 3786 β”‚
β”‚   26    β”‚   'un-bracket-notation'    β”‚ 3889 β”‚
β”‚   27    β”‚    'un-numeric-literal'    β”‚ 3931 β”‚
β”‚   28    β”‚       'smart-rename'       β”‚ 3941 β”‚
β”‚   29    β”‚   'un-optional-chaining'   β”‚ 4200 β”‚
β”‚   30    β”‚     'un-indirect-call'     β”‚ 4226 β”‚
β”‚   31    β”‚          'lebab'           β”‚ 4235 β”‚
β”‚   32    β”‚ 'un-sequence-expression-2' β”‚ 4408 β”‚
β”‚   33    β”‚   'un-flip-comparisons'    β”‚ 4549 β”‚
β”‚   34    β”‚ 'un-sequence-expression-1' β”‚ 4666 β”‚
β”‚   35    β”‚    'un-runtime-helper'     β”‚ 5408 β”‚
β”‚   36    β”‚          'un-esm'          β”‚ 5475 β”‚
β”‚   37    β”‚      'un-parameters'       β”‚ 6522 β”‚
β”‚   38    β”‚       'smart-inline'       β”‚ 7163 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”˜
Error: ENOENT: no such file or directory, open 'out/perf.json'
    at Object.openSync (node:fs:592:3)
    at Object.writeFileSync (node:fs:2323:35)
    at Object.writeFileSync (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/jsonfile/index.js:78:13)
    at writePerfStats (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/cli.cjs:1676:21)
    at nonInteractive (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/cli.cjs:1653:7)
    at async Object.handler (/Users/devalias/.npm/_npx/adacf4dfcf214674/node_modules/@wakaru/cli/dist/cli.cjs:1301:5) {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: 'out/perf.json'
}

Add CLI tool

Just stumbled across this project today, and it looks really great (both the online IDE, and the layout/structure/functionality of the code)

I was thinking it would be super useful if there was a CLI interface as well, to make it easier to run against local files without needing to write wrapper code each time.

Generate 'reverse sourcemap' from unminified output, that can be used directly with the original minified bundle

Another sourcemap related idea I had (which probably deserves it's own issue) is that it would be cool to be able to 'retroactively generate a sourcemap) for a webapp, based on the unminified output from wakaru; such that we could than take that sourcemap, and apply it to the original minified web app source for debugging the live app.

Originally posted by @0xdevalias in #34 (comment)

`@wakaru/unminify` CLI nests output in an extra folder

Just tried out the CLI on a webpack'd chunk (Ref), and it seems that @wakaru/unminify wrapped the output directory I specified in an extra folder.

First I unpacked it:

β‡’ cd ./unpacked/_next/static/chunks

β‡’ npx @wakaru/unpacker 496.js -o ./496-unpacked

Then I unminified it:

β‡’ npx @wakaru/unminify ./496-unpacked/* -o ./496-unminified

# ..snip..

I expected that the unminified files would have been in ./496-unminified/*.js, but they actually ended up in ./496-unminified/496-unpacked/*.js

Performance improvement and potential tools to adopt

I have a 5Mb sized file that needs to be processed, I've tried running it directly in node and it takes up to 21G of memory space in some transform processing.

However, in some of the processing, it may fail, so maybe it would be possible to save the data that was processed successfully? And use it again next time?

[smart-rename] improve `handleReactRename`

Currently smart-rename's implementation has a handleReactRename function that appears to have renames for:

  • const uContext = o.createContext(u);
  • const uRef = o.useRef(u);
  • const [e, SetE] = o.useState(0);

I figured I would use this as a bit of a meta-issue for capturing improvements that could be made to this smart-rename for React.

Context

If not otherwise specified, the webpack code I am looking at to derive my examples is the following (Ref), after using the CLI to unpack it to ./496-unpacked, and then unminify it to ./496-unminified:

β‡’ cd ./unpacked/_next/static/chunks

β‡’ npx @wakaru/unpacker 496.js -o ./496-unpacked
# ..snip..

β‡’ npx @wakaru/unminify ./496-unpacked/* -o ./496-unminified
# ..snip..

TODO

  • useState (but actually it ends up being more about @swc/helpers) (Ref)
    • See also: #50

See Also

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

dockerfile
Dockerfile
npm
package.json
  • ast-types ^0.16.1
  • recast ^0.23.4
packages/ast-utils/package.json
  • jscodeshift ^0.15.0
packages/ds/package.json
packages/playground/package.json
  • @codemirror/lang-javascript ^6.2.1
  • @codemirror/state ^6.2.1
  • @codemirror/theme-one-dark ^6.1.2
  • @codemirror/view ^6.21.3
  • @fortawesome/fontawesome-svg-core ^6.4.2
  • @fortawesome/free-brands-svg-icons ^6.4.2
  • @fortawesome/free-regular-svg-icons ^6.4.2
  • @fortawesome/free-solid-svg-icons ^6.4.2
  • @fortawesome/vue-fontawesome ^3.0.3
  • @headlessui/vue ^1.7.16
  • @heroicons/vue ^2.0.18
  • @vueuse/core ^10.4.1
  • assert ^2.1.0
  • codemirror ^6.0.1
  • os ^0.1.2
  • splitpanes ^3.1.5
  • vue ^3.3.4
  • vue-codemirror ^6.1.1
  • vue-router 4.2.5
packages/test-utils/package.json
  • jscodeshift ^0.15.0
  • vitest ^1.0.0-beta.1
packages/unminify/package.json
  • @babel/core ^7.23.0
  • @babel/helper-validator-identifier ^7.22.20
  • @babel/preset-env ^7.22.20
  • @babel/types ^7.23.0
  • fs-extra ^11.1.1
  • globby ^11.1.0
  • jscodeshift ^0.15.0
  • lebab ^3.2.3
  • picocolors ^1.0.0
  • prettier ^2.8.8
  • yargs ^17.7.2
packages/unpacker/package.json
  • ast-types ^0.16.1
  • jscodeshift ^0.15.0
  • picocolors ^1.0.0

  • Check this box to trigger a request for Renovate to run again on this repository

[module-detection / smart-rename] `styled-components` / `Tailwind-Styled-Component` libs

This relates to the 'module-detection' feature described in the following issue:


While looking through some decompiled code in a rather complex webpack bundled app, I've identified what seems to be the styled-components library (or something very similar to it):

It would be cool to be able to handle styled-components when reversing code.


This was originally based on the code I mentioned in the following comment:

A more general high level version of 'module detection' that this feature relates to is described in:


Edit: I've also captured all of my below notes on the following gist for easier future reference:

Download transformed files

Is it possible to download all the transformed files in one go, so that I can view them in my local IDE instead?

My use-case has ~170 files, it would be a pain to have to download each of them manually.

add a 'module graph'

Let me share a bit of my current thoughts on this:

  1. Introducing module graph: Like Webpack and other bundlers, a module graph can help us unminify/rename identifiers and exports from bottom to top.
  2. Based on 1, the steps gonna be like [unpacked] -> [???] -> [unminify]. This new step will build the module graph, do module scanning, rename the file smartly, and provide this information to unminify.
  3. In the module graph, we can have a map for all exported names and top-level variables/functions, which also allows the user to guide the tool to improve the mapping.
  4. Module graph also brings the possibility of cross-module renaming. For example, un-indirect-call shall detect some pattern and rename the minified export name back to the real name.
  5. I like the idea of "AST fingerprinting". This can also be used in module scanning to replace the current regex implementation.

It's ok to not link this response everywhere as I'm still thinking about this. And it should be moved to a new issue.

Originally posted by @pionxzh in #34 (comment)

See Also

Support detect and replace `microsoft/tslib`'s `runtime-helpers`

Spinning this out into a new issue so it doesn't get lost among the old one, but follow through to the original comment for more of the deep dive/evidence that lead to this being figured out:

Searching my webpacked code (Ref) for functions that have a similar format to the helpers:

β‡’ find . -type f -exec grep -E '_[[:alnum:]_]+\s*:\s*function' {} +

# ..snip..
# These don't seem to be @swc/helpers related?
./653.js:          __assign: function () {
./653.js:          __asyncDelegator: function () {
./653.js:          __asyncGenerator: function () {
./653.js:          __asyncValues: function () {
./653.js:          __await: function () {
./653.js:          __awaiter: function () {
./653.js:          __classPrivateFieldGet: function () {
./653.js:          __classPrivateFieldIn: function () {
./653.js:          __classPrivateFieldSet: function () {
./653.js:          __createBinding: function () {
./653.js:          __decorate: function () {
./653.js:          __exportStar: function () {
./653.js:          __extends: function () {
./653.js:          __generator: function () {
./653.js:          __importDefault: function () {
./653.js:          __importStar: function () {
./653.js:          __makeTemplateObject: function () {
./653.js:          __metadata: function () {
./653.js:          __param: function () {
./653.js:          __read: function () {
./653.js:          __rest: function () {
./653.js:          __spread: function () {
./653.js:          __spreadArray: function () {
./653.js:          __spreadArrays: function () {
./653.js:          __values: function () {
# ..snip..
# These don't seem to be @swc/helpers related?
./1f110208.js:          __parse: function (e, t) {
./1f110208.js:          __renderToHTMLTree: function (e, t) {
./1f110208.js:          __setFontMetrics: function (e, t) {
./1f110208.js:          __defineMacro: function (e, t) {
# ..snip..
# These do seem to be @swc/helpers related
./main.js:          _async_to_generator: function () {
./main.js:          _class_call_check: function () {
./main.js:          _construct: function () {
./main.js:          _create_class: function () {
./main.js:          _create_super: function () {
./main.js:          _inherits: function () {
./main.js:          _interop_require_default: function () {
./main.js:          _interop_require_wildcard: function () {
./main.js:          _object_spread: function () {
./main.js:          _object_spread_props: function () {
./main.js:          _object_without_properties: function () {
./main.js:          _sliced_to_array: function () {
./main.js:          _to_consumable_array: function () {
./main.js:          _ts_generator: function () {
./main.js:          _type_of: function () {
./main.js:          _wrap_native_super: function () {
# ..snip..
# These don't seem to be @swc/helpers related?
./pages/payments/success.js:          __assign: function () {
./pages/payments/success.js:          __asyncDelegator: function () {
./pages/payments/success.js:          __asyncGenerator: function () {
./pages/payments/success.js:          __asyncValues: function () {
./pages/payments/success.js:          __await: function () {
./pages/payments/success.js:          __awaiter: function () {
./pages/payments/success.js:          __classPrivateFieldGet: function () {
./pages/payments/success.js:          __classPrivateFieldIn: function () {
./pages/payments/success.js:          __classPrivateFieldSet: function () {
./pages/payments/success.js:          __createBinding: function () {
./pages/payments/success.js:          __decorate: function () {
./pages/payments/success.js:          __exportStar: function () {
./pages/payments/success.js:          __extends: function () {
./pages/payments/success.js:          __generator: function () {
./pages/payments/success.js:          __importDefault: function () {
./pages/payments/success.js:          __importStar: function () {
./pages/payments/success.js:          __makeTemplateObject: function () {
./pages/payments/success.js:          __metadata: function () {
./pages/payments/success.js:          __param: function () {
./pages/payments/success.js:          __read: function () {
./pages/payments/success.js:          __rest: function () {
./pages/payments/success.js:          __spread: function () {
./pages/payments/success.js:          __spreadArray: function () {
./pages/payments/success.js:          __spreadArrays: function () {
./pages/payments/success.js:          __values: function () {
# ..snip..

For the __ helpers that aren't @swc/helpers related, I did a GitHub code search:

And it looks like they are probably related to microsoft/tslib:

Originally posted by @0xdevalias in #50 (comment)

See Also

smart-rename for React `forwardRef` (+ remove layer of indirection on destructuring assignment)

It would be cool if smart-rename was able to handle React's forwardRef:

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
  // ...
});

Based on that example, we can see that the 2nd param of the React component passed to the forwardRef function is the ref being forwarded; which we can then use within the smart-rename.


Looking at my sample webpack'd code (Ref), after using the CLI to unpack it to ./496-unpacked, and then unminify it to ./496-unminified:

β‡’ cd ./unpacked/_next/static/chunks

β‡’ npx @wakaru/unpacker 496.js -o ./496-unpacked
# ..snip..

β‡’ npx @wakaru/unminify ./496-unpacked/* -o ./496-unminified
# ..snip..

Looking at module-10604.js, we can see that it's a React component using forwardRef:

Unpacked:

var r = require(39324),
  // ..snip..
  l = require(70079),
  // ..snip..

exports.Z = l.forwardRef(function (e, t) {
  var n = e.name,
    i = e.placeholder,
    u = e.type,
    c = e.displayName,
    h = e.onChange,
    g = e.onBlur,
    m = e.value,
    p = e.saveOnBlur,
    v = e.icon,
    x = e.onInputIconClick,
    b = e.className,
    y = e.autoComplete,
    w = e.autoFocus,
    j = e.onPressEnter,
  // ..snip..
});

Unminified:

// ..snip..
const l = require(70079);

const { useState, useCallback, useEffect } = l;
// ..snip..

export const Z = l.forwardRef((e, t) => {
  const {
    name,
    placeholder,
    type,
    displayName,
    onChange,
    onBlur,
    value,
    saveOnBlur,
    icon,
    onInputIconClick,
    className,
    autoComplete,
    autoFocus,
    onPressEnter,
  } = e;
  // ..snip..
});

If we had a smart-rename for this, then we could rename e to eProps (or just props), and t to tForwardedRef (or just forwardedRef), and the unminified output could look something more like this:

// ..snip..
const l = require(70079);

const { useState, useCallback, useEffect } = l;
// ..snip..

export const Z = l.forwardRef((eProps, tForwardedRef) => {
  const {
    name,
    placeholder,
    type,
    displayName,
    onChange,
    onBlur,
    value,
    saveOnBlur,
    icon,
    onInputIconClick,
    className,
    autoComplete,
    autoFocus,
    onPressEnter,
  } = eProps;
  // ..snip..
});

This could be improved even further if we removed the layer of indirection on the destructuring assignment on the import of l, as well as the React component props e (which might be a bug/edge case limitation of smart-inline, as I would have expected it to do it)

With that, the output could look more like:

// ..snip..
const { useState, useCallback, useEffect, forwardRef } = require(70079);
// ..snip..

export const Z = forwardRef(
  (
    {
      name,
      placeholder,
      type,
      displayName,
      onChange,
      onBlur,
      value,
      saveOnBlur,
      icon,
      onInputIconClick,
      className,
      autoComplete,
      autoFocus,
      onPressEnter,
    },
    tForwardedRef
  ) => {
    // ..snip..
  }
);

(Note: I'm not 100% sure if we can safely fold the l.forwardRef into the import like I showed in my above example, as it doesn't seem to use the (0, l.useEffect) style call pattern.. but as far as how it would be used in real world code.. my above example is fairly canonical)

Support parallel running

In the early stage, we actually ran multiple workers at the same time to achieve "parallel running". But it will hit on some confusing errors that the browser might not support running too many web workers at the same time. So it was removed.

We should bring this back with better control over the amount of workers spawned. This should be applied to both the web playground and the CLI.

Concern: the concurrent memory usage will increase and might cause OOM, maybe a CLI option can be a good idea?

Update

CLI has supported --concurrency flag for parallel running.

[module-detection] js-xss

This relates to the 'module-detection' feature described in the following issue:

Overview

Code

Unminifying this source (Ref):

Original (prettified)
// unpacked/_next/static/chunks/pages/_app.js, lines 55420-55441
    138: function (U, B, G) {
      var Y = G(56855),
        V = G(43310),
        Z = G(91611);
      function J(U, B) {
        return new Z(B).process(U);
      }
      ((B = U.exports = J).filterXSS = J),
        (B.FilterXSS = Z),
        (function () {
          for (var U in Y) B[U] = Y[U];
          for (var G in V) B[G] = V[G];
        })(),
        "undefined" != typeof window && (window.filterXSS = U.exports),
        (function () {
          return (
            "undefined" != typeof self &&
            "undefined" != typeof DedicatedWorkerGlobalScope &&
            self instanceof DedicatedWorkerGlobalScope
          );
        })() && (self.filterXSS = U.exports);
    },
Source (unpacked)
// module-138.js
var Y = require(56855);
var V = require(43310);
var Z = require(91611);
function J(U, B) {
  return new Z(B).process(U);
}
((exports = module.exports = J).filterXSS = J),
  (exports.FilterXSS = Z),
  (function () {
    for (var U in Y) exports[U] = Y[U];
    for (var G in V) exports[G] = V[G];
  })(),
  "undefined" != typeof window && (window.filterXSS = module.exports),
  (function () {
    return (
      "undefined" != typeof self &&
      "undefined" != typeof DedicatedWorkerGlobalScope &&
      self instanceof DedicatedWorkerGlobalScope
    );
  })() && (self.filterXSS = module.exports);

Transformed (unminified)

// module-138.js
import Y from "module-56855.js";
import V from "module-43310.js";
import Z from "module-91611.js";

export function filterXSS(U, B) {
  return new Z(B).process(U);
}

exports = filterXSS;
export default filterXSS;

(() => {
  for (const U in Y) {
    exports[U] = Y[U];
  }
  for (const G in V) {
    exports[G] = V[G];
  }
})();

if (typeof window != "undefined") {
  window.filterXSS = module.exports;
}

if (
  (() =>
    typeof self != "undefined" &&
    typeof DedicatedWorkerGlobalScope != "undefined" &&
    self instanceof DedicatedWorkerGlobalScope)()
) {
  self.filterXSS = module.exports;
}

Searching for some of those symbols on GitHub code search:

Of those results, this looks the most promising:

// js-xss/lib/index.js, lines 7-51
var DEFAULT = require("./default");
var parser = require("./parser");
var FilterXSS = require("./xss");

function filterXSS(html, options) {
  var xss = new FilterXSS(options);
  return xss.process(html);
}

exports = module.exports = filterXSS;
exports.filterXSS = filterXSS;
exports.FilterXSS = FilterXSS;

(function () {
  for (var i in DEFAULT) {
    exports[i] = DEFAULT[i];
  }
  for (var j in parser) {
    exports[j] = parser[j];
  }
})();

if (typeof window !== "undefined") {
  window.filterXSS = module.exports;
}

function isWorkerEnv() {
  return (
    typeof self !== "undefined" &&
    typeof DedicatedWorkerGlobalScope !== "undefined" &&
    self instanceof DedicatedWorkerGlobalScope
  );
}
if (isWorkerEnv()) {
  self.filterXSS = module.exports;
}

Based on the above, it looks like the following modules (in this bundle) are also related to js-xss:

Note: I haven't traced the related imports beyond the first level detailed above, but that would be useful to do to fully identify the related modules from this library (or it's dependencies).

jsx formats that cannot be handled.

using this code:

       (0,
       $ht.s)(edt).render((0,
       Zf.jsx)(i.StrictMode, {
           children: (0,
           Zf.jsx)(Qht, {})
       }))

I get this result:

(0, $ht.s)(edt).render(
 (0, Zf.jsx)(i.StrictMode, {
   children: (0, Zf.jsx)(Qht, {}),
 })
);

Probably the original code:

ReactDOM.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>,
    document.getElementById('root')
)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.