@plexcord-companion/webpack-chunk-parser 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ import { type ObjectLiteralExpression } from "typescript";
2
+ import { AstParser } from "@plexcord-companion/ast-parser";
3
+ export declare abstract class WebpackChunkParser extends AstParser {
4
+ /**
5
+ * @retuns the object with each module defined, should conform to Record<PropertyKey, (e, t, n) => void)>
6
+ */
7
+ abstract getModuleObject(): ObjectLiteralExpression | undefined;
8
+ /**
9
+ * ```js
10
+ * let __webpack_modules__ = {
11
+ * 123: function(module, exports, require) {
12
+ * // module
13
+ * }
14
+ * };
15
+ */
16
+ private tryParseChunkEntryPropertyAssignment;
17
+ /**
18
+ * ```js
19
+ * let __webpack_modules__ = {
20
+ * 123(module, exports, require) {
21
+ * // module
22
+ * }
23
+ * };
24
+ * ```
25
+ */
26
+ private tryParseChunkEntryMethodDecl;
27
+ private tryParseChunkEntry;
28
+ getDefinedModules(): Record<string, string> | undefined;
29
+ }
@@ -0,0 +1,71 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { isMethodDeclaration, isPropertyAssignment } from "typescript";
8
+ import { AstParser, isFunctionish, nonNull, tryParseStringOrNumberLiteral } from "@plexcord-companion/ast-parser";
9
+ import { Cache } from "@plexcord-companion/shared/decorators";
10
+ function fromEntries(obj, [k, v], _idx, _arr) {
11
+ obj[k] = v;
12
+ return obj;
13
+ }
14
+ export class WebpackChunkParser extends AstParser {
15
+ /**
16
+ * ```js
17
+ * let __webpack_modules__ = {
18
+ * 123: function(module, exports, require) {
19
+ * // module
20
+ * }
21
+ * };
22
+ */
23
+ tryParseChunkEntryPropertyAssignment(entry) {
24
+ const moduleId = tryParseStringOrNumberLiteral(entry.name);
25
+ if (!moduleId) {
26
+ return;
27
+ }
28
+ const moduleValue = entry.initializer;
29
+ if (!isFunctionish(moduleValue)) {
30
+ return;
31
+ }
32
+ return [moduleId, moduleValue.getText()];
33
+ }
34
+ /**
35
+ * ```js
36
+ * let __webpack_modules__ = {
37
+ * 123(module, exports, require) {
38
+ * // module
39
+ * }
40
+ * };
41
+ * ```
42
+ */
43
+ tryParseChunkEntryMethodDecl(entry) {
44
+ const moduleId = tryParseStringOrNumberLiteral(entry.name);
45
+ if (!moduleId) {
46
+ return;
47
+ }
48
+ return [moduleId, `function${entry.getText().substring(entry.name.getText().length)}`];
49
+ }
50
+ tryParseChunkEntry(entry) {
51
+ if (isPropertyAssignment(entry)) {
52
+ return this.tryParseChunkEntryPropertyAssignment(entry);
53
+ }
54
+ else if (isMethodDeclaration(entry)) {
55
+ return this.tryParseChunkEntryMethodDecl(entry);
56
+ }
57
+ return undefined;
58
+ }
59
+ getDefinedModules() {
60
+ return this
61
+ .getModuleObject()
62
+ ?.properties
63
+ .map(this.tryParseChunkEntry.bind(this))
64
+ .filter(nonNull)
65
+ .reduce((fromEntries), {});
66
+ }
67
+ }
68
+ __decorate([
69
+ Cache()
70
+ ], WebpackChunkParser.prototype, "getDefinedModules", null);
71
+ //# sourceMappingURL=WebpackChunkParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebpackChunkParser.js","sourceRoot":"","sources":["../src/WebpackChunkParser.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAgH,MAAM,YAAY,CAAC;AAErL,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAClH,OAAO,EAAE,KAAK,EAAE,MAAM,uCAAuC,CAAC;AAI9D,SAAS,WAAW,CAGlB,GAAiB,EAAE,CAAC,CAAC,EAAE,CAAC,CAA2B,EAAE,IAAY,EAAE,IAAuB;IACxF,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACX,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,OAAgB,kBAAmB,SAAQ,SAAS;IAMtD;;;;;;;OAOG;IACK,oCAAoC,CAAC,KAAyB;QAClE,MAAM,QAAQ,GAAG,6BAA6B,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE3D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,OAAO;QACX,CAAC;QAED,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;QAEtC,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;YAC9B,OAAO;QACX,CAAC;QAED,OAAO,CAAC,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;;;OAQG;IACK,4BAA4B,CAAC,KAAwB;QACzD,MAAM,QAAQ,GAAG,6BAA6B,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE3D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,OAAO;QACX,CAAC;QAGD,OAAO,CAAC,QAAQ,EAAE,WAAW,KAAK,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;IAEO,kBAAkB,CAAC,KAA+B;QACtD,IAAI,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC,oCAAoC,CAAC,KAAK,CAAC,CAAC;QAC5D,CAAC;aAAM,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;YACpC,OAAO,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAGM,iBAAiB;QACpB,OAAO,IAAI;aACN,eAAe,EAAE;YAClB,EAAE,UAAU;aACX,GAAG,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACvC,MAAM,CAAC,OAAO,CAAC;aACf,MAAM,CAAC,CAAA,WAA2B,CAAA,EAAE,EAAE,CAAC,CAAC;IACjD,CAAC;CACJ;AARU;IADN,KAAK,EAAE;2DAQP"}
@@ -0,0 +1,8 @@
1
+ import { type ObjectLiteralExpression } from "typescript";
2
+ import { WebpackChunkParser } from "./WebpackChunkParser.js";
3
+ export declare class WebpackLazyChunkParser extends WebpackChunkParser {
4
+ private get pushCall();
5
+ private assertOneEntry;
6
+ getModuleObject(): ObjectLiteralExpression | undefined;
7
+ get chunkId(): string | undefined;
8
+ }
@@ -0,0 +1,85 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { isArrayLiteralExpression, isCallExpression, isExpressionStatement, isObjectLiteralExpression, isPropertyAccessExpression } from "typescript";
8
+ import { isDirective, tryParseStringOrNumberLiteral } from "@plexcord-companion/ast-parser/util";
9
+ import { Cache, CacheGetter } from "@plexcord-companion/shared/decorators";
10
+ import { WebpackChunkParser } from "./WebpackChunkParser.js";
11
+ export class WebpackLazyChunkParser extends WebpackChunkParser {
12
+ get pushCall() {
13
+ const topLevelStatements = this.sourceFile.statements.filter((stmt) => !isDirective(stmt));
14
+ // we only expect one top-level statement
15
+ if (topLevelStatements.length !== 1) {
16
+ return;
17
+ }
18
+ const [stmt] = topLevelStatements;
19
+ if (!isExpressionStatement(stmt)) {
20
+ return;
21
+ }
22
+ const call = stmt.expression;
23
+ if (!isCallExpression(call)) {
24
+ return;
25
+ }
26
+ const { arguments: args, expression: funcExpr } = call;
27
+ // ensure push call
28
+ {
29
+ if (!isPropertyAccessExpression(funcExpr)) {
30
+ return;
31
+ }
32
+ const { expression: _pushToGlobal, name: pushIdent } = funcExpr;
33
+ if (!pushIdent) {
34
+ return;
35
+ }
36
+ if (pushIdent.text !== "push") {
37
+ return;
38
+ }
39
+ }
40
+ if (args.length !== 1) {
41
+ return;
42
+ }
43
+ return call;
44
+ }
45
+ assertOneEntry() {
46
+ const [arg] = this.pushCall?.arguments ?? [];
47
+ if (!arg || !isArrayLiteralExpression(arg)) {
48
+ return;
49
+ }
50
+ const { elements } = arg;
51
+ if (elements.length !== 2) {
52
+ return;
53
+ }
54
+ const [idTuple, modulesObject] = elements;
55
+ return [idTuple, modulesObject];
56
+ }
57
+ getModuleObject() {
58
+ const [, modulesArg] = this.assertOneEntry() ?? [];
59
+ if (!modulesArg || !isObjectLiteralExpression(modulesArg)) {
60
+ return;
61
+ }
62
+ return modulesArg;
63
+ }
64
+ get chunkId() {
65
+ const [idTuple] = this.assertOneEntry() ?? [];
66
+ if (!idTuple || !isArrayLiteralExpression(idTuple)) {
67
+ return;
68
+ }
69
+ const { elements: [idExpr] } = idTuple;
70
+ return tryParseStringOrNumberLiteral(idExpr);
71
+ }
72
+ }
73
+ __decorate([
74
+ CacheGetter()
75
+ ], WebpackLazyChunkParser.prototype, "pushCall", null);
76
+ __decorate([
77
+ Cache()
78
+ ], WebpackLazyChunkParser.prototype, "assertOneEntry", null);
79
+ __decorate([
80
+ Cache()
81
+ ], WebpackLazyChunkParser.prototype, "getModuleObject", null);
82
+ __decorate([
83
+ CacheGetter()
84
+ ], WebpackLazyChunkParser.prototype, "chunkId", null);
85
+ //# sourceMappingURL=WebpackLazyChunkParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebpackLazyChunkParser.js","sourceRoot":"","sources":["../src/WebpackLazyChunkParser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { VariableInfo } from "ts-api-utils";
2
+ import { type ObjectLiteralExpression, type PropertyName } from "typescript";
3
+ import type { JSHashEntry } from "./types.js";
4
+ import { WebpackChunkParser } from "./WebpackChunkParser.js";
5
+ export declare class WebpackMainChunkParser extends WebpackChunkParser {
6
+ get __webpack_require__(): VariableInfo | undefined;
7
+ get __webpack_modules__(): VariableInfo | undefined;
8
+ getJsChunkHashes(): JSHashEntry[];
9
+ getBuildNumber(): string | undefined;
10
+ /**
11
+ * Get the id of the entrypoint module.
12
+ * @returns The id of the entrypoint module, if found
13
+ * @returns undefined if the id could not be found
14
+ */
15
+ getEntrypointId(): string | undefined;
16
+ protected tryParseHashMapKey(node: PropertyName): string | undefined;
17
+ private tryParseHashMapValue;
18
+ private parseHashMapEntry;
19
+ getModuleObject(): ObjectLiteralExpression | undefined;
20
+ }
@@ -0,0 +1,186 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { isArrowFunction, isCallExpression, isElementAccessExpression, isIdentifier, isNumericLiteral, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isStringLiteralLike, isVariableDeclaration } from "typescript";
8
+ import { findParent, isAssignmentExpression, isBinaryPlusExpression, isFunctionish, lastChild, nonNull, tryParseStringOrNumberLiteral } from "@plexcord-companion/ast-parser/util";
9
+ import { Cache, CacheGetter } from "@plexcord-companion/shared/decorators";
10
+ import { WebpackChunkParser } from "./WebpackChunkParser.js";
11
+ const BUILD_MODULE_REGEX = /Trying to open a changelog for an invalid build number/;
12
+ const BUILD_NUMBER_REGEX = /(?:parseInt\("|"Trying to open a changelog for an invalid build number )(\d+?)"\)/;
13
+ const KNOWN_BUILD_MODULE_IDS = Object.freeze(["128014", "446023"]);
14
+ function isValidModuleId(id) {
15
+ return isStringLiteralLike(id) || isNumericLiteral(id);
16
+ }
17
+ export class WebpackMainChunkParser extends WebpackChunkParser {
18
+ get __webpack_require__() {
19
+ for (const [ident, info] of this.vars) {
20
+ if (ident.text === "__webpack_require__") {
21
+ return info;
22
+ }
23
+ }
24
+ }
25
+ get __webpack_modules__() {
26
+ for (const [ident, info] of this.vars) {
27
+ if (ident.text === "__webpack_modules__") {
28
+ return info;
29
+ }
30
+ }
31
+ }
32
+ getJsChunkHashes() {
33
+ const uses = this.__webpack_require__?.uses;
34
+ if (!uses) {
35
+ return [];
36
+ }
37
+ let uFunc;
38
+ foundU: {
39
+ for (const { location: { parent } } of uses) {
40
+ if (!isPropertyAccessExpression(parent)) {
41
+ continue;
42
+ }
43
+ // webpack js chunk name->id function
44
+ if (parent.name.text !== "u") {
45
+ continue;
46
+ }
47
+ const maybeAssign = parent.parent;
48
+ if (!isAssignmentExpression(maybeAssign) || !isFunctionish(maybeAssign.right)) {
49
+ continue;
50
+ }
51
+ uFunc = maybeAssign.right;
52
+ break foundU;
53
+ }
54
+ return [];
55
+ }
56
+ if (!isArrowFunction(uFunc)) {
57
+ return [];
58
+ }
59
+ const { body: ret } = uFunc;
60
+ // expect body to be BinExp>[BinExp>["" + {id:hash}[id]] + ".js"]
61
+ if (!isBinaryPlusExpression(ret)) {
62
+ return [];
63
+ }
64
+ const { left: concatWithHashMap } = ret;
65
+ if (!isBinaryPlusExpression(concatWithHashMap)) {
66
+ return [];
67
+ }
68
+ // {id:hash}[id]
69
+ const { right: hashMapAccess } = concatWithHashMap;
70
+ if (!isElementAccessExpression(hashMapAccess)) {
71
+ return [];
72
+ }
73
+ const hashMap = lastChild(hashMapAccess.expression, isObjectLiteralExpression);
74
+ if (!hashMap) {
75
+ return [];
76
+ }
77
+ return hashMap
78
+ .properties
79
+ .map(this.parseHashMapEntry.bind(this))
80
+ .filter(nonNull);
81
+ }
82
+ getBuildNumber() {
83
+ const m = this.getDefinedModules();
84
+ if (!m) {
85
+ return;
86
+ }
87
+ for (const maybeId of KNOWN_BUILD_MODULE_IDS) {
88
+ const moduleText = m[maybeId];
89
+ if (BUILD_MODULE_REGEX.test(moduleText)) {
90
+ const [, id] = BUILD_NUMBER_REGEX.exec(moduleText) ?? [];
91
+ if (!id) {
92
+ return;
93
+ }
94
+ return id;
95
+ }
96
+ }
97
+ }
98
+ /**
99
+ * Get the id of the entrypoint module.
100
+ * @returns The id of the entrypoint module, if found
101
+ * @returns undefined if the id could not be found
102
+ */
103
+ getEntrypointId() {
104
+ const wreq = this.__webpack_require__;
105
+ if (!wreq) {
106
+ return;
107
+ }
108
+ // var __webpack_exports__ = __webpack_require__(id);
109
+ for (const { location: { parent: call } } of wreq.uses) {
110
+ if (!call
111
+ || !isCallExpression(call)
112
+ || call.arguments.length !== 1) {
113
+ continue;
114
+ }
115
+ const [maybeId] = call.arguments;
116
+ if (!isValidModuleId(maybeId)) {
117
+ continue;
118
+ }
119
+ const { parent: decl } = call;
120
+ if (!decl
121
+ || !isVariableDeclaration(decl)
122
+ || !isIdentifier(decl.name)
123
+ || decl.name.text !== "__webpack_exports__") {
124
+ continue;
125
+ }
126
+ return maybeId.text;
127
+ }
128
+ }
129
+ tryParseHashMapKey(node) {
130
+ return tryParseStringOrNumberLiteral(node);
131
+ }
132
+ tryParseHashMapValue(node) {
133
+ if (!isStringLiteralLike(node)) {
134
+ return;
135
+ }
136
+ return node.text;
137
+ }
138
+ parseHashMapEntry(node) {
139
+ if (!isPropertyAssignment(node)) {
140
+ return;
141
+ }
142
+ const id = this.tryParseHashMapKey(node.name);
143
+ if (!id) {
144
+ return;
145
+ }
146
+ const hash = this.tryParseHashMapValue(node.initializer);
147
+ if (!hash) {
148
+ return;
149
+ }
150
+ return [id, hash];
151
+ }
152
+ getModuleObject() {
153
+ const wpModules = this.__webpack_modules__;
154
+ if (!wpModules) {
155
+ return;
156
+ }
157
+ const { declarations } = wpModules;
158
+ if (declarations.length !== 1) {
159
+ return;
160
+ }
161
+ const wpModulesDecl = findParent(declarations[0], isVariableDeclaration)?.initializer;
162
+ if (!wpModulesDecl || !isObjectLiteralExpression(wpModulesDecl)) {
163
+ return;
164
+ }
165
+ return wpModulesDecl;
166
+ }
167
+ }
168
+ __decorate([
169
+ CacheGetter()
170
+ ], WebpackMainChunkParser.prototype, "__webpack_require__", null);
171
+ __decorate([
172
+ CacheGetter()
173
+ ], WebpackMainChunkParser.prototype, "__webpack_modules__", null);
174
+ __decorate([
175
+ Cache()
176
+ ], WebpackMainChunkParser.prototype, "getJsChunkHashes", null);
177
+ __decorate([
178
+ Cache()
179
+ ], WebpackMainChunkParser.prototype, "getBuildNumber", null);
180
+ __decorate([
181
+ Cache()
182
+ ], WebpackMainChunkParser.prototype, "getEntrypointId", null);
183
+ __decorate([
184
+ Cache()
185
+ ], WebpackMainChunkParser.prototype, "getModuleObject", null);
186
+ //# sourceMappingURL=WebpackMainChunkParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebpackMainChunkParser.js","sourceRoot":"","sources":["../src/WebpackMainChunkParser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export type { JSHashEntry, ModuleEntry, } from "./types.js";
2
+ export { WebpackChunkParser, } from "./WebpackChunkParser.js";
3
+ export { WebpackLazyChunkParser, } from "./WebpackLazyChunkParser.js";
4
+ export { WebpackMainChunkParser, } from "./WebpackMainChunkParser.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { WebpackChunkParser, } from "./WebpackChunkParser.js";
2
+ export { WebpackLazyChunkParser, } from "./WebpackLazyChunkParser.js";
3
+ export { WebpackMainChunkParser, } from "./WebpackMainChunkParser.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACH,kBAAkB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACH,sBAAsB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACH,sBAAsB,GACzB,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export type ModuleEntry = [id: string, contents: string];
2
+ export type JSHashEntry = [id: string, hash: string];
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@plexcord-companion/webpack-chunk-parser",
3
+ "version": "2.0.0",
4
+ "description": "",
5
+ "author": {
6
+ "name": "MutanPlex",
7
+ "url": "https://mutanplex.com"
8
+ },
9
+ "main": "index.js",
10
+ "exports": {
11
+ "./*": "./dist/*.js",
12
+ ".": "./dist/index.js"
13
+ },
14
+ "keywords": [],
15
+ "license": "LGPL-3.0-or-later",
16
+ "dependencies": {
17
+ "@plexcord-companion/ast-parser": "2.0.0",
18
+ "@plexcord-companion/shared": "2.0.0"
19
+ },
20
+ "peerDependencies": {
21
+ "ts-api-utils": "^2.1.0",
22
+ "typescript": "^5.9.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^24.6.0",
26
+ "vitest": "^3.2.4"
27
+ },
28
+ "scripts": {
29
+ "preversion": "mkdir .git || :",
30
+ "bump": "npm version",
31
+ "postversion": "rmdir .git",
32
+ "build": "tsc -b && tsc-alias -f"
33
+ }
34
+ }
@@ -0,0 +1,85 @@
1
+ import { isMethodDeclaration, isPropertyAssignment, type MethodDeclaration, type ObjectLiteralElementLike, type ObjectLiteralExpression, type PropertyAssignment } from "typescript";
2
+
3
+ import { AstParser, isFunctionish, nonNull, tryParseStringOrNumberLiteral } from "@plexcord-companion/ast-parser";
4
+ import { Cache } from "@plexcord-companion/shared/decorators";
5
+
6
+ import type { ModuleEntry } from "./types";
7
+
8
+ function fromEntries<
9
+ K extends PropertyKey,
10
+ V,
11
+ >(obj: Record<K, V>, [k, v]: NoInfer<readonly [K, V]>, _idx: number, _arr: readonly [K, V][]): Record<K, V> {
12
+ obj[k] = v;
13
+ return obj;
14
+ }
15
+
16
+ export abstract class WebpackChunkParser extends AstParser {
17
+ /**
18
+ * @retuns the object with each module defined, should conform to Record<PropertyKey, (e, t, n) => void)>
19
+ */
20
+ abstract getModuleObject(): ObjectLiteralExpression | undefined;
21
+
22
+ /**
23
+ * ```js
24
+ * let __webpack_modules__ = {
25
+ * 123: function(module, exports, require) {
26
+ * // module
27
+ * }
28
+ * };
29
+ */
30
+ private tryParseChunkEntryPropertyAssignment(entry: PropertyAssignment): ModuleEntry | undefined {
31
+ const moduleId = tryParseStringOrNumberLiteral(entry.name);
32
+
33
+ if (!moduleId) {
34
+ return;
35
+ }
36
+
37
+ const moduleValue = entry.initializer;
38
+
39
+ if (!isFunctionish(moduleValue)) {
40
+ return;
41
+ }
42
+
43
+ return [moduleId, moduleValue.getText()];
44
+ }
45
+
46
+ /**
47
+ * ```js
48
+ * let __webpack_modules__ = {
49
+ * 123(module, exports, require) {
50
+ * // module
51
+ * }
52
+ * };
53
+ * ```
54
+ */
55
+ private tryParseChunkEntryMethodDecl(entry: MethodDeclaration): ModuleEntry | undefined {
56
+ const moduleId = tryParseStringOrNumberLiteral(entry.name);
57
+
58
+ if (!moduleId) {
59
+ return;
60
+ }
61
+
62
+
63
+ return [moduleId, `function${entry.getText().substring(entry.name.getText().length)}`];
64
+ }
65
+
66
+ private tryParseChunkEntry(entry: ObjectLiteralElementLike): ModuleEntry | undefined {
67
+ if (isPropertyAssignment(entry)) {
68
+ return this.tryParseChunkEntryPropertyAssignment(entry);
69
+ } else if (isMethodDeclaration(entry)) {
70
+ return this.tryParseChunkEntryMethodDecl(entry);
71
+ }
72
+ return undefined;
73
+ }
74
+
75
+ @Cache()
76
+ public getDefinedModules(): Record<string, string> | undefined {
77
+ return this
78
+ .getModuleObject()
79
+ ?.properties
80
+ .map(this.tryParseChunkEntry.bind(this))
81
+ .filter(nonNull)
82
+ .reduce(fromEntries<string, string>, {});
83
+ }
84
+ }
85
+
@@ -0,0 +1,97 @@
1
+ import { type CallExpression, type Expression, isArrayLiteralExpression, isCallExpression, isExpressionStatement, isObjectLiteralExpression, isPropertyAccessExpression, type ObjectLiteralExpression } from "typescript";
2
+
3
+ import { isDirective, tryParseStringOrNumberLiteral } from "@plexcord-companion/ast-parser/util";
4
+ import { Cache, CacheGetter } from "@plexcord-companion/shared/decorators";
5
+
6
+ import { WebpackChunkParser } from "./WebpackChunkParser";
7
+
8
+ export class WebpackLazyChunkParser extends WebpackChunkParser {
9
+ @CacheGetter()
10
+ private get pushCall(): CallExpression | undefined {
11
+ const topLevelStatements = this.sourceFile.statements.filter((stmt) => !isDirective(stmt));
12
+
13
+ // we only expect one top-level statement
14
+ if (topLevelStatements.length !== 1) {
15
+ return;
16
+ }
17
+
18
+ const [stmt] = topLevelStatements;
19
+
20
+ if (!isExpressionStatement(stmt)) {
21
+ return;
22
+ }
23
+
24
+ const call = stmt.expression;
25
+
26
+ if (!isCallExpression(call)) {
27
+ return;
28
+ }
29
+
30
+ const { arguments: args, expression: funcExpr } = call;
31
+
32
+ // ensure push call
33
+ {
34
+ if (!isPropertyAccessExpression(funcExpr)) {
35
+ return;
36
+ }
37
+
38
+ const { expression: _pushToGlobal, name: pushIdent } = funcExpr;
39
+
40
+ if (!pushIdent) {
41
+ return;
42
+ }
43
+ if (pushIdent.text !== "push") {
44
+ return;
45
+ }
46
+ }
47
+
48
+ if (args.length !== 1) {
49
+ return;
50
+ }
51
+
52
+ return call;
53
+ }
54
+
55
+ @Cache()
56
+ private assertOneEntry(): [Expression, Expression] | undefined {
57
+ const [arg] = this.pushCall?.arguments ?? [];
58
+
59
+ if (!arg || !isArrayLiteralExpression(arg)) {
60
+ return;
61
+ }
62
+
63
+ const { elements } = arg;
64
+
65
+ if (elements.length !== 2) {
66
+ return;
67
+ }
68
+
69
+ const [idTuple, modulesObject] = elements;
70
+
71
+ return [idTuple, modulesObject];
72
+ }
73
+
74
+ @Cache()
75
+ public override getModuleObject(): ObjectLiteralExpression | undefined {
76
+ const [, modulesArg] = this.assertOneEntry() ?? [];
77
+
78
+ if (!modulesArg || !isObjectLiteralExpression(modulesArg)) {
79
+ return;
80
+ }
81
+
82
+ return modulesArg;
83
+ }
84
+
85
+ @CacheGetter()
86
+ public get chunkId(): string | undefined {
87
+ const [idTuple] = this.assertOneEntry() ?? [];
88
+
89
+ if (!idTuple || !isArrayLiteralExpression(idTuple)) {
90
+ return;
91
+ }
92
+
93
+ const { elements: [idExpr] } = idTuple;
94
+
95
+ return tryParseStringOrNumberLiteral(idExpr);
96
+ }
97
+ }
@@ -0,0 +1,227 @@
1
+ import type { VariableInfo } from "ts-api-utils";
2
+ import { type Expression, isArrowFunction, isCallExpression, isElementAccessExpression, isIdentifier, isNumericLiteral, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isStringLiteralLike, isVariableDeclaration, type ObjectLiteralElementLike, type ObjectLiteralExpression, type PropertyName } from "typescript";
3
+
4
+ import type { Functionish } from "@plexcord-companion/ast-parser/types";
5
+ import { findParent, isAssignmentExpression, isBinaryPlusExpression, isFunctionish, lastChild, nonNull, tryParseStringOrNumberLiteral } from "@plexcord-companion/ast-parser/util";
6
+ import { Cache, CacheGetter } from "@plexcord-companion/shared/decorators";
7
+
8
+ import type { JSHashEntry } from "./types";
9
+ import { WebpackChunkParser } from "./WebpackChunkParser";
10
+
11
+ const BUILD_MODULE_REGEX = /Trying to open a changelog for an invalid build number/;
12
+ const BUILD_NUMBER_REGEX = /(?:parseInt\("|"Trying to open a changelog for an invalid build number )(\d+?)"\)/;
13
+ const KNOWN_BUILD_MODULE_IDS: ReadonlyArray<string> = Object.freeze(["128014", "446023"]);
14
+
15
+ function isValidModuleId(id: Expression) {
16
+ return isStringLiteralLike(id) || isNumericLiteral(id);
17
+ }
18
+
19
+ export class WebpackMainChunkParser extends WebpackChunkParser {
20
+ @CacheGetter()
21
+ get __webpack_require__(): VariableInfo | undefined {
22
+ for (const [ident, info] of this.vars) {
23
+ if (ident.text === "__webpack_require__") {
24
+ return info;
25
+ }
26
+ }
27
+ }
28
+
29
+ @CacheGetter()
30
+ get __webpack_modules__(): VariableInfo | undefined {
31
+ for (const [ident, info] of this.vars) {
32
+ if (ident.text === "__webpack_modules__") {
33
+ return info;
34
+ }
35
+ }
36
+ }
37
+
38
+ @Cache()
39
+ public getJsChunkHashes(): JSHashEntry[] {
40
+ const uses = this.__webpack_require__?.uses;
41
+
42
+ if (!uses) {
43
+ return [];
44
+ }
45
+
46
+ let uFunc: Functionish | undefined;
47
+
48
+ foundU: {
49
+ for (const { location: { parent } } of uses) {
50
+ if (!isPropertyAccessExpression(parent)) {
51
+ continue;
52
+ }
53
+
54
+ // webpack js chunk name->id function
55
+ if (parent.name.text !== "u") {
56
+ continue;
57
+ }
58
+
59
+ const maybeAssign = parent.parent;
60
+
61
+ if (!isAssignmentExpression(maybeAssign) || !isFunctionish(maybeAssign.right)) {
62
+ continue;
63
+ }
64
+
65
+ uFunc = maybeAssign.right;
66
+ break foundU;
67
+ }
68
+ return [];
69
+ }
70
+
71
+ if (!isArrowFunction(uFunc)) {
72
+ return [];
73
+ }
74
+
75
+ const { body: ret } = uFunc;
76
+
77
+ // expect body to be BinExp>[BinExp>["" + {id:hash}[id]] + ".js"]
78
+ if (!isBinaryPlusExpression(ret)) {
79
+ return [];
80
+ }
81
+
82
+ const { left: concatWithHashMap } = ret;
83
+
84
+ if (!isBinaryPlusExpression(concatWithHashMap)) {
85
+ return [];
86
+ }
87
+
88
+ // {id:hash}[id]
89
+ const { right: hashMapAccess } = concatWithHashMap;
90
+
91
+ if (!isElementAccessExpression(hashMapAccess)) {
92
+ return [];
93
+ }
94
+
95
+ const hashMap = lastChild(hashMapAccess.expression, isObjectLiteralExpression);
96
+
97
+ if (!hashMap) {
98
+ return [];
99
+ }
100
+
101
+ return hashMap
102
+ .properties
103
+ .map(this.parseHashMapEntry.bind(this))
104
+ .filter(nonNull);
105
+ }
106
+
107
+ @Cache()
108
+ public getBuildNumber(): string | undefined {
109
+ const m = this.getDefinedModules();
110
+
111
+ if (!m) {
112
+ return;
113
+ }
114
+
115
+ for (const maybeId of KNOWN_BUILD_MODULE_IDS) {
116
+ const moduleText = m[maybeId];
117
+
118
+ if (BUILD_MODULE_REGEX.test(moduleText)) {
119
+ const [, id] = BUILD_NUMBER_REGEX.exec(moduleText) ?? [];
120
+
121
+ if (!id) {
122
+ return;
123
+ }
124
+
125
+ return id;
126
+ }
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Get the id of the entrypoint module.
132
+ * @returns The id of the entrypoint module, if found
133
+ * @returns undefined if the id could not be found
134
+ */
135
+ @Cache()
136
+ public getEntrypointId(): string | undefined {
137
+ const wreq = this.__webpack_require__;
138
+
139
+ if (!wreq) {
140
+ return;
141
+ }
142
+
143
+ // var __webpack_exports__ = __webpack_require__(id);
144
+ for (const { location: { parent: call } } of wreq.uses) {
145
+ if (
146
+ !call
147
+ || !isCallExpression(call)
148
+ || call.arguments.length !== 1
149
+ ) {
150
+ continue;
151
+ }
152
+
153
+ const [maybeId] = call.arguments;
154
+
155
+ if (!isValidModuleId(maybeId)) {
156
+ continue;
157
+ }
158
+
159
+ const { parent: decl } = call;
160
+
161
+ if (
162
+ !decl
163
+ || !isVariableDeclaration(decl)
164
+ || !isIdentifier(decl.name)
165
+ || decl.name.text !== "__webpack_exports__"
166
+ ) {
167
+ continue;
168
+ }
169
+
170
+ return maybeId.text;
171
+ }
172
+ }
173
+
174
+ protected tryParseHashMapKey(node: PropertyName): string | undefined {
175
+ return tryParseStringOrNumberLiteral(node);
176
+ }
177
+
178
+ private tryParseHashMapValue(node: Expression): string | undefined {
179
+ if (!isStringLiteralLike(node)) {
180
+ return;
181
+ }
182
+ return node.text;
183
+ }
184
+
185
+ private parseHashMapEntry(node: ObjectLiteralElementLike): JSHashEntry | undefined {
186
+ if (!isPropertyAssignment(node)) {
187
+ return;
188
+ }
189
+
190
+ const id = this.tryParseHashMapKey(node.name);
191
+
192
+ if (!id) {
193
+ return;
194
+ }
195
+
196
+ const hash = this.tryParseHashMapValue(node.initializer);
197
+
198
+ if (!hash) {
199
+ return;
200
+ }
201
+
202
+ return [id, hash];
203
+ }
204
+
205
+ @Cache()
206
+ override getModuleObject(): ObjectLiteralExpression | undefined {
207
+ const wpModules = this.__webpack_modules__;
208
+
209
+ if (!wpModules) {
210
+ return;
211
+ }
212
+
213
+ const { declarations } = wpModules;
214
+
215
+ if (declarations.length !== 1) {
216
+ return;
217
+ }
218
+
219
+ const wpModulesDecl = findParent(declarations[0], isVariableDeclaration)?.initializer;
220
+
221
+ if (!wpModulesDecl || !isObjectLiteralExpression(wpModulesDecl)) {
222
+ return;
223
+ }
224
+
225
+ return wpModulesDecl;
226
+ }
227
+ }
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ export type {
2
+ JSHashEntry, ModuleEntry,
3
+ } from "./types";
4
+ export {
5
+ WebpackChunkParser,
6
+ } from "./WebpackChunkParser";
7
+ export {
8
+ WebpackLazyChunkParser,
9
+ } from "./WebpackLazyChunkParser";
10
+ export {
11
+ WebpackMainChunkParser,
12
+ } from "./WebpackMainChunkParser";
package/src/types.ts ADDED
@@ -0,0 +1,2 @@
1
+ export type ModuleEntry = [id: string, contents: string];
2
+ export type JSHashEntry = [id: string, hash: string];
@@ -0,0 +1,219 @@
1
+ // This file exists for quick and easy reference to discord's webpack types
2
+ /* eslint-disable */
3
+ /*!
4
+ * @plexcord/discord-types
5
+ * Copyright (c) 2024 Vendicated, Nuckyz and contributors
6
+ * Copyright (c) 2025 MutanPlex
7
+ * SPDX-License-Identifier: LGPL-3.0-or-later
8
+ */
9
+
10
+ export type ModuleExports = any;
11
+
12
+ export type Module = {
13
+ id: PropertyKey;
14
+ loaded: boolean;
15
+ exports: ModuleExports;
16
+ };
17
+
18
+ /** exports can be anything, however initially it is always an empty object */
19
+ export type ModuleFactory = (this: ModuleExports, module: Module, exports: ModuleExports, require: WebpackRequire) => void;
20
+
21
+ /** Keys here can be symbols too, but we can't properly type them */
22
+ export type AsyncModulePromise = Promise<ModuleExports> & {
23
+ "__webpack_queues__": (fnQueue: ((queue: any[]) => any)) => any;
24
+ "__webpack_exports__": ModuleExports;
25
+ "__webpack_error__"?: any;
26
+ };
27
+
28
+ export type AsyncModuleBody = (
29
+ handleAsyncDependencies: (deps: AsyncModulePromise[]) =>
30
+ Promise<() => ModuleExports[]> | (() => ModuleExports[]),
31
+ asyncResult: (error?: any) => void
32
+ ) => Promise<void>;
33
+
34
+ export type EnsureChunkHandlers = {
35
+ /**
36
+ * Ensures the js file for this chunk is loaded, or starts to load if it's not.
37
+ * @param chunkId The chunk id
38
+ * @param promises The promises array to add the loading promise to
39
+ */
40
+ j: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
41
+ /**
42
+ * Ensures the css file for this chunk is loaded, or starts to load if it's not.
43
+ * @param chunkId The chunk id
44
+ * @param promises The promises array to add the loading promise to. This array will likely contain the promise of the js file too
45
+ */
46
+ css: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
47
+ /**
48
+ * Trigger for prefetching next chunks. This is called after ensuring a chunk is loaded and internally looks up
49
+ * a map to see if the chunk that just loaded has next chunks to prefetch.
50
+ *
51
+ * Note that this does not add an extra promise to the promises array, and instead only executes the prefetching after
52
+ * calling Promise.all on the promises array.
53
+ * @param chunkId The chunk id
54
+ * @param promises The promises array of ensuring the chunk is loaded
55
+ */
56
+ prefetch: (this: EnsureChunkHandlers, chunkId: PropertyKey, promises: Promise<void[]>) => void;
57
+ };
58
+
59
+ export type PrefetchChunkHandlers = {
60
+ /**
61
+ * Prefetches the js file for this chunk.
62
+ * @param chunkId The chunk id
63
+ */
64
+ j: (this: PrefetchChunkHandlers, chunkId: PropertyKey) => void;
65
+ };
66
+
67
+ export type ScriptLoadDone = (event: Event) => void;
68
+
69
+ export type OnChunksLoaded = ((this: WebpackRequire, result: any, chunkIds: PropertyKey[] | undefined | null, callback: () => any, priority: number) => any) & {
70
+ /** Check if a chunk has been loaded */
71
+ j: (this: OnChunksLoaded, chunkId: PropertyKey) => boolean;
72
+ };
73
+
74
+ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
75
+ /** The module factories, where all modules that have been loaded are stored (pre-loaded or loaded by lazy chunks) */
76
+ m: Record<PropertyKey, ModuleFactory>;
77
+ /** The module cache, where all modules which have been WebpackRequire'd are stored */
78
+ c: Record<PropertyKey, Module>;
79
+ // /**
80
+ // * Export star. Sets properties of "fromObject" to "toObject" as getters that return the value from "fromObject", like this:
81
+ // * @example
82
+ // * const fromObject = { a: 1 };
83
+ // * Object.keys(fromObject).forEach(key => {
84
+ // * if (key !== "default" && !Object.hasOwn(toObject, key)) {
85
+ // * Object.defineProperty(toObject, key, {
86
+ // * get: () => fromObject[key],
87
+ // * enumerable: true
88
+ // * });
89
+ // * }
90
+ // * });
91
+ // * @returns fromObject
92
+ // */
93
+ // es: (this: WebpackRequire, fromObject: AnyRecord, toObject: AnyRecord) => AnyRecord;
94
+ /**
95
+ * Creates an async module. A module that which has top level await, or requires an export from an async module.
96
+ *
97
+ * The body function must be an async function. "module.exports" will become an {@link AsyncModulePromise}.
98
+ *
99
+ * The body function will be called with a function to handle requires that import from an async module, and a function to resolve this async module. An example on how to handle async dependencies:
100
+ * @example
101
+ * const factory = (module, exports, wreq) => {
102
+ * wreq.a(module, async (handleAsyncDependencies, asyncResult) => {
103
+ * try {
104
+ * const asyncRequireA = wreq(...);
105
+ *
106
+ * const asyncDependencies = handleAsyncDependencies([asyncRequire]);
107
+ * const [requireAResult] = asyncDependencies.then != null ? (await asyncDependencies)() : asyncDependencies;
108
+ *
109
+ * // Use the required module
110
+ * console.log(requireAResult);
111
+ *
112
+ * // Mark this async module as resolved
113
+ * asyncResult();
114
+ * } catch(error) {
115
+ * // Mark this async module as rejected with an error
116
+ * asyncResult(error);
117
+ * }
118
+ * }, false); // false because our module does not have an await after dealing with the async requires
119
+ * }
120
+ */
121
+ a: (this: WebpackRequire, module: Module, body: AsyncModuleBody, hasAwaitAfterDependencies?: boolean) => void;
122
+ /** getDefaultExport function for compatibility with non-harmony modules */
123
+ n: (this: WebpackRequire, exports: any) => () => ModuleExports;
124
+ /**
125
+ * Create a fake namespace object, useful for faking an __esModule with a default export.
126
+ *
127
+ * mode & 1: Value is a module id, require it
128
+ *
129
+ * mode & 2: Merge all properties of value into the namespace
130
+ *
131
+ * mode & 4: Return value when already namespace object
132
+ *
133
+ * mode & 16: Return value when it's Promise-like
134
+ *
135
+ * mode & (8|1): Behave like require
136
+ */
137
+ t: (this: WebpackRequire, value: any, mode: number) => any;
138
+ /**
139
+ * Define getter functions for harmony exports. For every prop in "definiton" (the module exports), set a getter in "exports" for the getter function in the "definition", like this:
140
+ * @example
141
+ * const exports = {};
142
+ * const definition = { exportName: () => someExportedValue };
143
+ * for (const key in definition) {
144
+ * if (Object.hasOwn(definition, key) && !Object.hasOwn(exports, key)) {
145
+ * Object.defineProperty(exports, key, {
146
+ * get: definition[key],
147
+ * enumerable: true
148
+ * });
149
+ * }
150
+ * }
151
+ * // exports is now { exportName: someExportedValue } (but each value is actually a getter)
152
+ */
153
+ d: (this: WebpackRequire, exports: Record<PropertyKey, any>, definiton: Record<PropertyKey, () => ModuleExports>) => void;
154
+ /** The ensure chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
155
+ f: EnsureChunkHandlers;
156
+ /**
157
+ * The ensure chunk function, it ensures a chunk is loaded, or loads if needed.
158
+ * Internally it uses the handlers in {@link WebpackRequire.f} to load/ensure the chunk is loaded.
159
+ */
160
+ e: (this: WebpackRequire, chunkId: PropertyKey) => Promise<void[]>;
161
+ /** The prefetch chunk handlers, which are used to prefetch the files of the chunks */
162
+ F: PrefetchChunkHandlers;
163
+ /**
164
+ * The prefetch chunk function.
165
+ * Internally it uses the handlers in {@link WebpackRequire.F} to prefetch a chunk.
166
+ */
167
+ E: (this: WebpackRequire, chunkId: PropertyKey) => void;
168
+ /** Get the filename for the css part of a chunk */
169
+ k: (this: WebpackRequire, chunkId: PropertyKey) => string;
170
+ /** Get the filename for the js part of a chunk */
171
+ u: (this: WebpackRequire, chunkId: PropertyKey) => string;
172
+ /** The global object, will likely always be the window */
173
+ g: typeof globalThis;
174
+ /** Harmony module decorator. Decorates a module as an ES Module, and prevents Node.js "module.exports" from being set */
175
+ hmd: (this: WebpackRequire, module: Module) => any;
176
+ /** Shorthand for Object.prototype.hasOwnProperty */
177
+ o: typeof Object.prototype.hasOwnProperty;
178
+ /**
179
+ * Function to load a script tag. "done" is called when the loading has finished or a timeout has occurred.
180
+ * "done" will be attached to existing scripts loading if src === url or data-webpack === `${uniqueName}:${key}`,
181
+ * so it will be called when that existing script finishes loading.
182
+ */
183
+ l: (this: WebpackRequire, url: string, done: ScriptLoadDone, key?: string | number, chunkId?: PropertyKey) => void;
184
+ /** Defines __esModule on the exports, marking ES Modules compatibility as true */
185
+ r: (this: WebpackRequire, exports: ModuleExports) => void;
186
+ /** Node.js module decorator. Decorates a module as a Node.js module */
187
+ nmd: (this: WebpackRequire, module: Module) => any;
188
+ /**
189
+ * Register deferred code which will be executed when the passed chunks are loaded.
190
+ *
191
+ * If chunkIds is defined, it defers the execution of the callback and returns undefined.
192
+ *
193
+ * If chunkIds is undefined, and no deferred code exists or can be executed, it returns the value of the result argument.
194
+ *
195
+ * If chunkIds is undefined, and some deferred code can already be executed, it returns the result of the callback function of the last deferred code.
196
+ *
197
+ * When (priority & 1) it will wait for all other handlers with lower priority to be executed before itself is executed.
198
+ */
199
+ O: OnChunksLoaded;
200
+ /**
201
+ * Instantiate a wasm instance with source using "wasmModuleHash", and importObject "importsObj", and then assign the exports of its instance to "exports".
202
+ * @returns The exports argument, but now assigned with the exports of the wasm instance
203
+ */
204
+ v: (this: WebpackRequire, exports: ModuleExports, wasmModuleId: any, wasmModuleHash: string, importsObj?: WebAssembly.Imports) => Promise<any>;
205
+ /** Bundle public path, where chunk files are stored. Used by other methods which load chunks to obtain the full asset url */
206
+ p: string;
207
+ /** The runtime id of the current runtime */
208
+ j: string;
209
+ /** Document baseURI or WebWorker location.href */
210
+ b: string;
211
+
212
+ /* rspack only */
213
+
214
+ /** rspack version */
215
+ rv: (this: WebpackRequire) => string;
216
+ /** rspack unique id */
217
+ ruid: string;
218
+ };
219
+