Skip to content

Commit

Permalink
feat: transform json modules
Browse files Browse the repository at this point in the history
  • Loading branch information
j4k0xb committed Jan 13, 2024
1 parent 31c2e70 commit 3a91796
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 8 deletions.
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
12 changes: 12 additions & 0 deletions packages/webcrack/src/unpack/test/samples/webpack-4-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(window['webpackJsonp'] = window['webpackJsonp'] || []).push([
[],
[
function (module, exports, __webpack_require__) {
var json = __webpack_require__(1);
console.log(json);
},
function (module, exports) {
module.exports = JSON.parse('{"foo":"bar"}');
},
],
]);
44 changes: 44 additions & 0 deletions packages/webcrack/src/unpack/test/samples/webpack-4-json.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
WebpackBundle {
"chunks": [
WebpackChunk {
"chunkIds": [],
"entryIds": [],
"modules": Map {
"0" => WebpackModule {
"ast": import * as json from "1";
console.log(json);,
"id": "0",
"isEntry": false,
"path": "0.js",
},
"1" => WebpackModule {
"ast": module.exports = {
foo: "bar"
};,
"id": "1",
"isEntry": false,
"path": "1.json",
},
},
},
],
"entryId": "",
"modules": Map {
"0" => WebpackModule {
"ast": import * as json from "1";
console.log(json);,
"id": "0",
"isEntry": false,
"path": "0.js",
},
"1" => WebpackModule {
"ast": module.exports = {
foo: "bar"
};,
"id": "1",
"isEntry": false,
"path": "1.json",
},
},
"type": "webpack",
}
12 changes: 12 additions & 0 deletions packages/webcrack/src/unpack/test/samples/webpack-5-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(window['webpackJsonp'] = window['webpackJsonp'] || []).push([
[],
[
function (module, exports, __webpack_require__) {
var json = __webpack_require__(1);
console.log(json);
},
function (module, exports) {
module.exports = { foo: 'bar' };
},
],
]);
44 changes: 44 additions & 0 deletions packages/webcrack/src/unpack/test/samples/webpack-5-json.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
WebpackBundle {
"chunks": [
WebpackChunk {
"chunkIds": [],
"entryIds": [],
"modules": Map {
"0" => WebpackModule {
"ast": import * as json from "1";
console.log(json);,
"id": "0",
"isEntry": false,
"path": "0.js",
},
"1" => WebpackModule {
"ast": module.exports = {
foo: "bar"
};,
"id": "1",
"isEntry": false,
"path": "1.json",
},
},
},
],
"entryId": "",
"modules": Map {
"0" => WebpackModule {
"ast": import * as json from "1";
console.log(json);,
"id": "0",
"isEntry": false,
"path": "0.js",
},
"1" => WebpackModule {
"ast": module.exports = {
foo: "bar"
};,
"id": "1",
"isEntry": false,
"path": "1.json",
},
},
"type": "webpack",
}
43 changes: 43 additions & 0 deletions packages/webcrack/src/unpack/webpack/json-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import traverse from '@babel/traverse';
import type * as t from '@babel/types';
import * as m from '@codemod/matchers';
import { constKey, constMemberExpression } from '../../ast-utils';

/**
* @returns The parsed JSON value if the AST is a JSON module, otherwise undefined.
*/
export function transformJsonModule(ast: t.File): unknown {
const jsonValue = m.or(
m.stringLiteral(),
m.numericLiteral(),
m.unaryExpression('-', m.numericLiteral()),
m.booleanLiteral(),
m.nullLiteral(),
m.matcher<t.ObjectExpression | t.ArrayExpression>(
(node) => jsonObject.match(node) || jsonArray.match(node),
),
);
const jsonObject = m.objectExpression(
m.arrayOf(m.objectProperty(constKey(), jsonValue)),
);
const jsonArray = m.arrayExpression(m.arrayOf(jsonValue));
const matcher = m.expressionStatement(
m.assignmentExpression(
'=',
constMemberExpression('module', 'exports'),
m.or(jsonObject, jsonArray),
),
);

if (ast.program.body.length === 1 && matcher.match(ast.program.body[0])) {
let result: unknown;
traverse(ast, {
noScope: true,
'ObjectExpression|ArrayExpression'(path) {
result = path.evaluate().value;
path.stop();
},
});
return result;
}
}
22 changes: 15 additions & 7 deletions packages/webcrack/src/unpack/webpack/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { constMemberExpression } from '../../ast-utils/matcher';
import { Module } from '../module';
import type { FunctionPath } from './common-matchers';
import { ImportExportManager } from './import-export-manager';
import { transformJsonModule } from './json-module';
import { default as definePropertyGetters } from './runtime/define-property-getters';
import getDefaultExport from './runtime/get-default-export';
import global from './runtime/global';
Expand All @@ -16,7 +17,8 @@ import varInjections from './var-injections';
export class WebpackModule extends Module {
#importExportManager: ImportExportManager;
// TODO: expose to public API
#sourceType: 'commonjs' | 'esm' = 'commonjs';
#sourceType: 'commonjs' | 'esm' | 'json' = 'commonjs';
#json: unknown;

constructor(id: string, ast: FunctionPath, isEntry: boolean) {
// TODO: refactor
Expand Down Expand Up @@ -54,12 +56,18 @@ export class WebpackModule extends Module {
if (moduleBinding) renameFast(moduleBinding, 'module');
if (exportsBinding) renameFast(exportsBinding, 'exports');

// this.removeDefineESM();
// // FIXME: some bundles don't define __esModule but still declare esm exports
// // https://github.com/0xdevalias/chatgpt-source-watch/blob/main/orig/_next/static/chunks/167-121de668c4456907.js
// if (this.#sourceType === 'esm') {
// this.convertExportsToESM();
// }
const json = transformJsonModule(this.ast);
if (json) {
this.#sourceType = 'json';
this.#json = json;
this.path = this.path.replace(/\.js$/, '.json');
}
}

override get code() {
return this.#sourceType === 'json'
? JSON.stringify(this.#json, null, 2)
: super.code;
}

/**
Expand Down

0 comments on commit 3a91796

Please sign in to comment.