Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpack refactor #50

Draft
wants to merge 81 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
020fb00
wip
j4k0xb Jan 13, 2024
821bf89
feat: webpack chunk class
j4k0xb Jan 13, 2024
7369268
fix: remove trailing webpack comment
j4k0xb Jan 13, 2024
95e519a
feat: export default expression
j4k0xb Jan 13, 2024
c41576b
refactor: store multiple chunk ids in a chunk
j4k0xb Jan 13, 2024
a3e126e
refactor: group export tests by webpack 4/5
j4k0xb Jan 13, 2024
a169879
perf: stop traversing once webpack chunk or module is found
j4k0xb Jan 13, 2024
463f47c
perf: skip traversing in var-injections
j4k0xb Jan 13, 2024
6d9cbe0
feat: export analysis
j4k0xb Jan 13, 2024
9a773f9
refactor: import export manager
j4k0xb Jan 13, 2024
ecc0a1f
fix: export already exported variable
j4k0xb Jan 13, 2024
639d633
feat: default export
j4k0xb Jan 13, 2024
18667e8
feat: track exports
j4k0xb Jan 13, 2024
d79e94b
fix: export variable named and default
j4k0xb Jan 13, 2024
5251afd
refactor: track export names only
j4k0xb Jan 13, 2024
336e9f1
refactor: export default
j4k0xb Jan 13, 2024
cf01037
refactor: remove RequireVar name
j4k0xb Jan 13, 2024
fbf2be0
fix: re-export all name
j4k0xb Jan 13, 2024
9913e50
refactor: use babel template
j4k0xb Jan 13, 2024
8050dd1
refactor: add unexpected export comment instead of throwing
j4k0xb Jan 13, 2024
4f5679f
feat: track imports
j4k0xb Jan 13, 2024
df71e78
feat: remove `__webpack_require__.hmd` and `__webpack_require__.nmd`
j4k0xb Jan 13, 2024
8d0faac
feat: remove `__webpack_require__.r`
j4k0xb Jan 13, 2024
21417e5
feat: replace `__webpack_require__.o`
j4k0xb Jan 13, 2024
16ac04d
refactor: `__webpack_require__.d`
j4k0xb Jan 13, 2024
01315f9
feat: replace `__webpack_require__.g`
j4k0xb Jan 13, 2024
bbe7574
refactor: use this instead of a state param in transform.run
j4k0xb Jan 13, 2024
9efe754
perf: use references for webpack transforms
j4k0xb Jan 13, 2024
7b1c7e2
feat: apply webpack runtime transforms
j4k0xb Jan 13, 2024
dce09bd
docs: webpack runtime comments
j4k0xb Jan 13, 2024
ea6d667
feat: imports
j4k0xb Jan 13, 2024
691794b
docs: todo comments
j4k0xb Jan 13, 2024
542b014
fix: imports being mixed up
j4k0xb Jan 13, 2024
7731634
fix: don't remove require var without references
j4k0xb Jan 13, 2024
afbabb8
fix: normalize module paths
j4k0xb Jan 13, 2024
024df05
feat: more specific webpack parameter names, handle custom namespace …
j4k0xb Jan 13, 2024
2075348
fix: properly implement concatenated module exports
j4k0xb Jan 13, 2024
4faef28
feat: default import
j4k0xb Jan 13, 2024
7a7c00c
refactor: import/export management
j4k0xb Jan 13, 2024
90d92cc
test: add more import tests
j4k0xb Jan 13, 2024
a2f7419
refactor: remove unused files
j4k0xb Jan 13, 2024
069cb49
test: add nested var injections
j4k0xb Jan 13, 2024
94c8d14
style: lint consistent-type-imports
j4k0xb Jan 13, 2024
ad3fed2
test: add failing inlined variable export test case
j4k0xb Jan 13, 2024
e1d54b2
wip
j4k0xb Jan 13, 2024
27f5813
test: more exports
j4k0xb Jan 13, 2024
4d8cf35
refactor: remove redundant test
j4k0xb Jan 13, 2024
8641259
test: fix many import test cases
j4k0xb Jan 13, 2024
bbedc10
fix: indirect call import
j4k0xb Jan 13, 2024
949248e
refactor
j4k0xb Jan 13, 2024
da538fd
fix: default import
j4k0xb Jan 13, 2024
5420156
fix: imports
j4k0xb Jan 13, 2024
cc20371
test: enable remaining export tests
j4k0xb Jan 13, 2024
6975fb0
test: mixed import/require
j4k0xb Jan 13, 2024
b7543c4
feat: sort import specifiers alphabetically
j4k0xb Jan 13, 2024
f1ac839
refactor: separate namespace and named export properties
j4k0xb Jan 13, 2024
5f778fc
feat: better default import
j4k0xb Jan 13, 2024
ed1b93e
refactor(perf): avoid container matcher for webpack function
j4k0xb Jan 13, 2024
1d14033
refactor: separate webpack 4 and 5 matching
j4k0xb Jan 13, 2024
10074a7
refactor: remove unused webpack samples
j4k0xb Jan 13, 2024
780b891
feat: rename module and exports in commonjs
j4k0xb Jan 13, 2024
34c5938
refactor: remove unused code
j4k0xb Jan 13, 2024
1f137d6
fix: support multiple namespace imports
j4k0xb Jan 13, 2024
d8a2381
Revert "refactor: separate webpack 4 and 5 matching"
j4k0xb Jan 13, 2024
39f9aaf
fix: multiple named imports
j4k0xb Jan 13, 2024
de98b60
fix: indirect calls for default import
j4k0xb Jan 13, 2024
cd26c95
fix: decompile jsx after unpacking the bundle
j4k0xb Jan 13, 2024
69c4f93
test: remove `__webpack_require__.r` in every import test
j4k0xb Jan 13, 2024
379017e
test: temporarily disable failing tests
j4k0xb Jan 13, 2024
31c2e70
feat: remove `__esModule` property
j4k0xb Jan 13, 2024
3a91796
feat: transform json modules
j4k0xb Jan 13, 2024
6a0e470
feat(playground): add json file icons
j4k0xb Jan 13, 2024
0238007
feat: inline external modules
j4k0xb Jan 15, 2024
0c9a328
fix: use `__webpack_module__` for json module matcher
j4k0xb Jan 15, 2024
b74ee88
test: re-enable unpack samples
j4k0xb Jan 15, 2024
51c9f10
fix: normalize with posix path
j4k0xb Jan 15, 2024
a3bb7f0
style: fix comments
j4k0xb Jan 15, 2024
4a28006
feat: expose export names in webpack module
j4k0xb Jan 15, 2024
3140dd0
test: update samples
j4k0xb Jan 15, 2024
7fd08bc
fix: path file extension for webpack development builds
j4k0xb Jan 22, 2024
4cc7b2c
fix: export variable as named and default
j4k0xb Jan 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/playground/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function App() {
...result.files.map((file) =>
monaco.editor.createModel(
file.code,
'javascript',
file.path.endsWith('.json') ? 'json' : 'javascript',
monaco.Uri.file(file.path),
),
),
Expand Down
29 changes: 23 additions & 6 deletions apps/playground/src/components/FileNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,38 @@ interface Props extends TreeNode {
onClick?: () => void;
}

// Icons are from https://github.com/vscode-icons/vscode-icons
export default function FileNode(props: Props) {
function handleClick(event: Event) {
event.stopPropagation();
props.onClick?.();
}

const icon = () =>
props.name.endsWith('.json') ? (
<svg width="16" height="16" class="inline" viewBox="0 0 32 32">
<path
d="M4.014,14.976a2.51,2.51,0,0,0,1.567-.518A2.377,2.377,0,0,0,6.386,13.1,15.261,15.261,0,0,0,6.6,10.156q.012-2.085.075-2.747a5.236,5.236,0,0,1,.418-1.686,3.025,3.025,0,0,1,.755-1.018A3.046,3.046,0,0,1,9,4.125,6.762,6.762,0,0,1,10.544,4h.7V5.96h-.387a2.338,2.338,0,0,0-1.723.468A3.4,3.4,0,0,0,8.709,8.52a36.054,36.054,0,0,1-.137,4.133,4.734,4.734,0,0,1-.768,2.06A4.567,4.567,0,0,1,6.1,16a3.809,3.809,0,0,1,1.992,1.754,8.861,8.861,0,0,1,.618,3.865q0,2.435.05,2.9A1.755,1.755,0,0,0,9.264,25.7a2.639,2.639,0,0,0,1.592.337h.387V28h-.7a5.655,5.655,0,0,1-1.773-.2,2.97,2.97,0,0,1-1.324-.93,3.353,3.353,0,0,1-.681-1.63A24.175,24.175,0,0,1,6.6,22.006,16.469,16.469,0,0,0,6.386,18.9a2.408,2.408,0,0,0-.805-1.361,2.489,2.489,0,0,0-1.567-.524Z"
style="fill:#f5de19"
/>
<path
d="M27.986,17.011a2.489,2.489,0,0,0-1.567.524,2.408,2.408,0,0,0-.805,1.361,16.469,16.469,0,0,0-.212,3.109,24.175,24.175,0,0,1-.169,3.234,3.353,3.353,0,0,1-.681,1.63,2.97,2.97,0,0,1-1.324.93,5.655,5.655,0,0,1-1.773.2h-.7V26.04h.387a2.639,2.639,0,0,0,1.592-.337,1.755,1.755,0,0,0,.506-1.186q.05-.462.05-2.9a8.861,8.861,0,0,1,.618-3.865A3.809,3.809,0,0,1,25.9,16a4.567,4.567,0,0,1-1.7-1.286,4.734,4.734,0,0,1-.768-2.06,36.054,36.054,0,0,1-.137-4.133,3.4,3.4,0,0,0-.425-2.092,2.338,2.338,0,0,0-1.723-.468h-.387V4h.7A6.762,6.762,0,0,1,23,4.125a3.046,3.046,0,0,1,1.149.581,3.025,3.025,0,0,1,.755,1.018,5.236,5.236,0,0,1,.418,1.686q.062.662.075,2.747a15.261,15.261,0,0,0,.212,2.947,2.377,2.377,0,0,0,.805,1.355,2.51,2.51,0,0,0,1.567.518Z"
style="fill:#f5de19"
/>
</svg>
) : (
<svg width="16" height="16" class="inline" viewBox="0 0 32 32">
<path
d="M18.774,19.7a3.727,3.727,0,0,0,3.376,2.078c1.418,0,2.324-.709,2.324-1.688,0-1.173-.931-1.589-2.491-2.272l-.856-.367c-2.469-1.052-4.11-2.37-4.11-5.156,0-2.567,1.956-4.52,5.012-4.52A5.058,5.058,0,0,1,26.9,10.52l-2.665,1.711a2.327,2.327,0,0,0-2.2-1.467,1.489,1.489,0,0,0-1.638,1.467c0,1.027.636,1.442,2.1,2.078l.856.366c2.908,1.247,4.549,2.518,4.549,5.376,0,3.081-2.42,4.769-5.671,4.769a6.575,6.575,0,0,1-6.236-3.5ZM6.686,20c.538.954,1.027,1.76,2.2,1.76,1.124,0,1.834-.44,1.834-2.15V7.975h3.422V19.658c0,3.543-2.078,5.156-5.11,5.156A5.312,5.312,0,0,1,3.9,21.688Z"
style="fill:#f5de19"
/>
</svg>
);

return (
<li>
<a title={props.path} onClick={handleClick}>
<svg width="16" height="16" class="inline" viewBox="0 0 32 32">
<path
d="M18.774,19.7a3.727,3.727,0,0,0,3.376,2.078c1.418,0,2.324-.709,2.324-1.688,0-1.173-.931-1.589-2.491-2.272l-.856-.367c-2.469-1.052-4.11-2.37-4.11-5.156,0-2.567,1.956-4.52,5.012-4.52A5.058,5.058,0,0,1,26.9,10.52l-2.665,1.711a2.327,2.327,0,0,0-2.2-1.467,1.489,1.489,0,0,0-1.638,1.467c0,1.027.636,1.442,2.1,2.078l.856.366c2.908,1.247,4.549,2.518,4.549,5.376,0,3.081-2.42,4.769-5.671,4.769a6.575,6.575,0,0,1-6.236-3.5ZM6.686,20c.538.954,1.027,1.76,2.2,1.76,1.124,0,1.834-.44,1.834-2.15V7.975h3.422V19.658c0,3.543-2.078,5.156-5.11,5.156A5.312,5.312,0,0,1,3.9,21.688Z"
style="fill:#f5de19"
/>
</svg>
{icon()}
{props.name}
</a>
</li>
Expand Down
2 changes: 1 addition & 1 deletion apps/playground/src/context/DeobfuscateContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ParentProps} from 'solid-js';
import type { ParentProps } from 'solid-js';
import { createContext, createSignal, useContext } from 'solid-js';
import type { Options } from 'webcrack';
import { evalCode } from '../sandbox';
Expand Down
2 changes: 1 addition & 1 deletion apps/playground/src/webcrack.worker.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Options, Sandbox} from 'webcrack';
import type { Options, Sandbox } from 'webcrack';
import { webcrack } from 'webcrack';

export type WorkerRequest =
Expand Down
15 changes: 15 additions & 0 deletions packages/webcrack/src/ast-utils/binding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Binding } from '@babel/traverse';
import type * as t from '@babel/types';

/**
* Remove a referencePath from a binding and decrement the amount of references.
*/
export function dereference(binding: Binding, reference: t.Node) {
const index = binding.referencePaths.findIndex(
(ref) => ref.node === reference,
);
if (index !== -1) {
binding.referencePaths.splice(index, 1);
binding.dereference();
}
}
21 changes: 20 additions & 1 deletion packages/webcrack/src/ast-utils/matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const iife = matchIife();
export const emptyIife = matchIife([]);

/**
* Matches both identifier properties and string literal computed properties
* Matches either `object.property` or `object['property']`
*/
export function constMemberExpression(
object: string | m.Matcher<t.Expression>,
Expand Down Expand Up @@ -107,6 +107,25 @@ export function findPath<T extends t.Node>(
return path.find((path) => matcher.match(path.node)) as NodePath<T> | null;
}

/**
* Matches a function expression or arrow function expression with a block body.
*/
export function anyFunctionExpression(
params?:
| m.Matcher<(t.Identifier | t.Pattern | t.RestElement)[]>
| (
| m.Matcher<t.Identifier>
| m.Matcher<t.Pattern>
| m.Matcher<t.RestElement>
)[],
body?: m.Matcher<t.BlockStatement>,
): m.Matcher<t.FunctionExpression | t.ArrowFunctionExpression> {
return m.or(
m.functionExpression(undefined, params, body),
m.arrowFunctionExpression(params, body),
);
}

/**
* Function expression matcher that captures the parameters
* and allows them to be referenced in the body.
Expand Down
2 changes: 2 additions & 0 deletions packages/webcrack/src/ast-utils/rename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import * as m from '@codemod/matchers';
import { codePreview } from './generator';

export function renameFast(binding: Binding, newName: string): void {
if (binding.identifier.name === newName) return;

binding.referencePaths.forEach((ref) => {
if (!ref.isIdentifier()) {
throw new Error(
Expand Down
10 changes: 5 additions & 5 deletions packages/webcrack/src/ast-utils/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export async function applyTransformAsync<TOptions>(
logger(`${transform.name}: started`);
const state: TransformState = { changes: 0 };

await transform.run?.(ast, state, options);
await transform.run?.call(state, ast, options);
if (transform.visitor)
traverse(ast, transform.visitor(options), undefined, state);

Expand All @@ -28,7 +28,7 @@ export function applyTransform<TOptions>(
): TransformState {
logger(`${transform.name}: started`);
const state: TransformState = { changes: 0 };
transform.run?.(ast, state, options);
transform.run?.call(state, ast, options);

if (transform.visitor) {
const visitor = transform.visitor(
Expand All @@ -53,7 +53,7 @@ export function applyTransforms(
const state: TransformState = { changes: 0 };

for (const transform of transforms) {
transform.run?.(ast, state);
transform.run?.call(state, ast, state);
}

const traverseOptions = transforms.flatMap((t) => t.visitor?.() ?? []);
Expand Down Expand Up @@ -93,13 +93,13 @@ export interface Transform<TOptions = unknown> {
name: string;
tags: Tag[];
scope?: boolean;
run?: (ast: Node, state: TransformState, options?: TOptions) => void;
run?: (this: TransformState, ast: Node, options?: TOptions) => void;
visitor?: (options?: TOptions) => Visitor<TransformState>;
}

export interface AsyncTransform<TOptions = unknown>
extends Transform<TOptions> {
run?: (ast: Node, state: TransformState, options?: TOptions) => Promise<void>;
run?: (this: TransformState, ast: Node, options?: TOptions) => Promise<void>;
}

export type Tag = 'safe' | 'unsafe';
12 changes: 6 additions & 6 deletions packages/webcrack/src/deobfuscate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default {
name: 'deobfuscate',
tags: ['unsafe'],
scope: true,
async run(ast, state, sandbox) {
async run(ast, sandbox) {
if (!sandbox) return;

const logger = debug('webcrack:deobfuscate');
Expand All @@ -44,29 +44,29 @@ export default {
const decoders = findDecoders(stringArray);
logger(`String Array Encodings: ${decoders.length}`);

state.changes += applyTransform(ast, inlineObjectProps).changes;
this.changes += applyTransform(ast, inlineObjectProps).changes;

for (const decoder of decoders) {
state.changes += applyTransform(
this.changes += applyTransform(
ast,
inlineDecoderWrappers,
decoder.path,
).changes;
}

const vm = new VMDecoder(sandbox, stringArray, decoders, rotator);
state.changes += (
this.changes += (
await applyTransformAsync(ast, inlineDecodedStrings, { vm })
).changes;

if (decoders.length > 0) {
stringArray.path.remove();
rotator?.remove();
decoders.forEach((decoder) => decoder.path.remove());
state.changes += 2 + decoders.length;
this.changes += 2 + decoders.length;
}

state.changes += applyTransforms(
this.changes += applyTransforms(
ast,
[mergeStrings, deadCode, controlFlowObject, controlFlowSwitch],
{ noScope: true },
Expand Down
4 changes: 2 additions & 2 deletions packages/webcrack/src/deobfuscate/inline-decoded-strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default {
name: 'inline-decoded-strings',
tags: ['unsafe'],
scope: true,
async run(ast, state, options) {
async run(ast, options) {
if (!options) return;

const calls = options.vm.decoders.flatMap((decoder) =>
Expand All @@ -27,6 +27,6 @@ export default {
call.addComment('leading', 'webcrack:decode_error');
}

state.changes += calls.length;
this.changes += calls.length;
},
} satisfies AsyncTransform<{ vm: VMDecoder }>;
6 changes: 3 additions & 3 deletions packages/webcrack/src/deobfuscate/inline-decoder-wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ export default {
name: 'inline-decoder-wrappers',
tags: ['unsafe'],
scope: true,
run(ast, state, decoder) {
run(ast, decoder) {
if (!decoder?.node.id) return;

const decoderName = decoder.node.id.name;
const decoderBinding = decoder.parentPath.scope.getBinding(decoderName);
if (decoderBinding) {
state.changes += inlineVariableAliases(decoderBinding).changes;
state.changes += inlineFunctionAliases(decoderBinding).changes;
this.changes += inlineVariableAliases(decoderBinding).changes;
this.changes += inlineFunctionAliases(decoderBinding).changes;
}
},
} satisfies Transform<NodePath<t.FunctionDeclaration>>;
22 changes: 7 additions & 15 deletions packages/webcrack/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,23 +154,15 @@ export async function webcrack(
}),
options.mangle && (() => applyTransform(ast, mangle)),
// TODO: Also merge unminify visitor (breaks selfDefending/debugProtection atm)
(options.deobfuscate || options.jsx) &&
(() => {
return applyTransforms(
ast,
[
// Have to run this after unminify to properly detect it
options.deobfuscate ? [selfDefending, debugProtection] : [],
options.jsx ? [jsx, jsxNew] : [],
].flat(),
{ noScope: true },
);
}),
options.deobfuscate &&
(() =>
applyTransforms(ast, [selfDefending, debugProtection], {
noScope: true,
})),
options.deobfuscate && (() => applyTransform(ast, mergeObjectAssignments)),
() => (outputCode = generate(ast)),
// Unpacking modifies the same AST and may result in imports not at top level
// so the code has to be generated before
options.unpack && (() => (bundle = unpackAST(ast, options.mappings(m)))),
options.jsx && (() => applyTransforms(ast, [jsx, jsxNew])),
() => (outputCode = generate(ast)),
].filter(Boolean) as (() => unknown)[];

for (let i = 0; i < stages.length; i++) {
Expand Down
3 changes: 1 addition & 2 deletions packages/webcrack/src/unpack/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class Bundle {
this.modules = modules;
}

// TODO: remove/deprecate (use module onResolve instead)
applyMappings(mappings: Record<string, m.Matcher<unknown>>): void {
const mappingPaths = Object.keys(mappings);
if (mappingPaths.length === 0) return;
Expand Down Expand Up @@ -85,6 +86,4 @@ export class Bundle {
}),
);
}

applyTransforms(): void {}
}
24 changes: 7 additions & 17 deletions packages/webcrack/src/unpack/index.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,30 @@
import { parse } from '@babel/parser';
import traverse, { visitors } from '@babel/traverse';
import type * as t from '@babel/types';
import type * as m from '@codemod/matchers';
import debug from 'debug';
import { unpackBrowserify } from './browserify';
import type { Bundle } from './bundle';
import { unpackWebpack } from './webpack';
import debug from 'debug';
import unpackWebpack4 from './webpack/unpack-webpack-4';
import unpackWebpack5 from './webpack/unpack-webpack-5';
import unpackWebpackChunk from './webpack/unpack-webpack-chunk';

export { Bundle } from './bundle';

export function unpack(
code: string,
mappings: Record<string, m.Matcher<unknown>> = {},
): Bundle | undefined {
const ast = parse(code, {
sourceType: 'unambiguous',
allowReturnOutsideFunction: true,
plugins: ['jsx'],
});
return unpackAST(ast, mappings);
}

export function unpackAST(
ast: t.Node,
mappings: Record<string, m.Matcher<unknown>> = {},
): Bundle | undefined {
const options: { bundle: Bundle | undefined } = { bundle: undefined };
const visitor = visitors.merge([
unpackWebpack.visitor(options),
unpackWebpack4.visitor(options),
unpackWebpack5.visitor(options),
unpackWebpackChunk.visitor(options),
unpackBrowserify.visitor(options),
]);
traverse(ast, visitor, undefined, { changes: 0 });
// TODO: applyTransforms(ast, [unpackWebpack, unpackBrowserify]) instead
if (options.bundle) {
options.bundle.applyMappings(mappings);
options.bundle.applyTransforms();
debug('webcrack:unpack')('Bundle:', options.bundle.type);
}
return options.bundle;
Expand Down
11 changes: 10 additions & 1 deletion packages/webcrack/src/unpack/module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type * as t from '@babel/types';
import { posix } from 'node:path';
import { generate } from '../ast-utils';

// eslint-disable-next-line @typescript-eslint/unbound-method
const { normalize, extname } = posix;

export class Module {
id: string;
isEntry: boolean;
Expand All @@ -15,7 +19,12 @@ export class Module {
this.id = id;
this.ast = ast;
this.isEntry = isEntry;
this.path = `./${isEntry ? 'index' : id}.js`;
this.path =
extname(id) === '' && isEntry ? 'index.js' : this.normalizePath(id);
}

private normalizePath(path: string): string {
return normalize(extname(path) === '' ? `${path}.js` : path);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/webcrack/src/unpack/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { dirname, join, relative } = posix;
export function relativePath(from: string, to: string): string {
if (to.startsWith('node_modules/')) return to.replace('node_modules/', '');
const relativePath = relative(dirname(from), to);
return relativePath.startsWith('.') ? relativePath : './' + relativePath;
return relativePath.startsWith('..') ? relativePath : './' + relativePath;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

exports[`path mapping 1`] = `
WebpackBundle {
"chunks": [],
"entryId": "2",
"modules": Map {
"1" => WebpackModule {
Expand Down
13 changes: 13 additions & 0 deletions packages/webcrack/src/unpack/test/commonjs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { test } from 'vitest';
import { testWebpackModuleTransform } from '.';

const expectJS = testWebpackModuleTransform();

test('rename module, exports and require', () =>
expectJS(`
__webpack_module__.exports = __webpack_require__("foo");
__webpack_exports__.foo = 1;
`).toMatchInlineSnapshot(`
module.exports = require("foo");
exports.foo = 1;
`));
Loading
Loading