@whitesev/utils 2.5.6 → 2.5.8

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,397 @@
1
+ // ==UserScript==
2
+ // @name ModuleRaid.js
3
+ // @namespace http://tampermonkey.net/
4
+ // @version 6.2.0
5
+ // @description 检索调用webpackJsonp模块,可指定检索的window
6
+ // @author empyrealtear
7
+ // @license MIT
8
+ // @original-script https://github.com/pixeldesu/moduleRaid
9
+ // ==/UserScript==
10
+
11
+
12
+ /**
13
+ * Main moduleRaid class
14
+ * @link https://scriptcat.org/zh-CN/script-show-page/2628
15
+ */
16
+ export class ModuleRaid {
17
+ /**
18
+ * moduleRaid constructor
19
+ *
20
+ * @example
21
+ * Constructing an instance without any arguments:
22
+ * ```ts
23
+ * const mR = new ModuleRaid()
24
+ * ```
25
+ *
26
+ * Constructing an instance with the optional `opts` object:
27
+ * ```ts
28
+ * const mR = new ModuleRaid({ entrypoint: 'webpackChunk_custom_name' })
29
+ * ```
30
+ *
31
+ * @param opts a object containing options to initialize moduleRaid with
32
+ * - **opts:**
33
+ * - _target_: the window object being searched for
34
+ * - _entrypoint_: the Webpack entrypoint present on the global window object
35
+ * - _debug_: whether debug mode is enabled or not
36
+ * - _strict_: whether strict mode is enabled or not
37
+ */
38
+ constructor(opts) {
39
+ /**
40
+ * A random generated module ID we use for injecting into Webpack
41
+ */
42
+ this.moduleID = Math.random().toString(36).substring(7);
43
+ /**
44
+ * An array containing different argument injection methods for
45
+ * Webpack (before version 4), and subsequently pulling out methods and modules
46
+ * @internal
47
+ */
48
+ this.functionArguments = [
49
+ [
50
+ [0],
51
+ [
52
+ (_e, _t, i) => {
53
+ this.modules = i.c;
54
+ this.constructors = i.m;
55
+ this.get = i;
56
+ },
57
+ ],
58
+ ],
59
+ [
60
+ [1e3],
61
+ {
62
+ [this.moduleID]: (_e, _t, i) => {
63
+ this.modules = i.c;
64
+ this.constructors = i.m;
65
+ this.get = i;
66
+ },
67
+ },
68
+ [[this.moduleID]],
69
+ ],
70
+ ];
71
+ /**
72
+ * An array containing different argument injection methods for
73
+ * Webpack (after version 4), and subsequently pulling out methods and modules
74
+ * @internal
75
+ */
76
+ this.arrayArguments = [
77
+ [
78
+ [this.moduleID],
79
+ {},
80
+ (e) => {
81
+ const mCac = e.m;
82
+ Object.keys(mCac).forEach((mod) => {
83
+ try {
84
+ this.modules[mod] = e(mod);
85
+ }
86
+ catch (err) {
87
+ this.log(`[arrayArguments/1] Failed to require(${mod}) with error:\n${err}\n${err.stack}`);
88
+ }
89
+ });
90
+ this.get = e;
91
+ },
92
+ ],
93
+ this.functionArguments[1],
94
+ ];
95
+ /**
96
+ * Storage for the modules we extracted from Webpack
97
+ */
98
+ this.modules = {};
99
+ /**
100
+ * Storage for the constructors we extracted from Webpack
101
+ */
102
+ this.constructors = [];
103
+ let options = {
104
+ target: window,
105
+ entrypoint: 'webpackJsonp',
106
+ debug: false,
107
+ strict: false,
108
+ };
109
+ if (typeof opts === 'object') {
110
+ options = Object.assign(Object.assign({}, options), opts);
111
+ }
112
+ this.target = options.target
113
+ this.entrypoint = options.entrypoint;
114
+ this.debug = options.debug;
115
+ this.strict = options.strict;
116
+ this.detectEntrypoint();
117
+ this.fillModules();
118
+ this.replaceGet();
119
+ this.setupPushEvent();
120
+ }
121
+ /**
122
+ * Debug logging method, outputs to the console when {@link ModuleRaid.debug} is true
123
+ *
124
+ * @param {*} message The message to be logged
125
+ * @internal
126
+ */
127
+ log(message) {
128
+ if (this.debug) {
129
+ console.warn(`[moduleRaid] ${message}`);
130
+ }
131
+ }
132
+ /**
133
+ * Method to set an alternative getter if we weren't able to extract __webpack_require__
134
+ * from Webpack
135
+ * @internal
136
+ */
137
+ replaceGet() {
138
+ if (this.get === null) {
139
+ this.get = (key) => this.modules[key];
140
+ }
141
+ }
142
+ /**
143
+ * Method that will try to inject a module into Webpack or get modules
144
+ * depending on it's success it might be more or less brute about it
145
+ * @internal
146
+ */
147
+ fillModules() {
148
+ if (typeof this.target[this.entrypoint] === 'function') {
149
+ this.functionArguments.forEach((argument, index) => {
150
+ try {
151
+ if (this.modules && Object.keys(this.modules).length > 0)
152
+ return;
153
+ this.target[this.entrypoint](...argument);
154
+ }
155
+ catch (err) {
156
+ this.log(`moduleRaid.functionArguments[${index}] failed:\n${err}\n${err.stack}`);
157
+ }
158
+ });
159
+ }
160
+ else {
161
+ this.arrayArguments.forEach((argument, index) => {
162
+ try {
163
+ if (this.modules && Object.keys(this.modules).length > 0)
164
+ return;
165
+ this.target[this.entrypoint].push(argument);
166
+ }
167
+ catch (err) {
168
+ this.log(`Pushing moduleRaid.arrayArguments[${index}] into ${this.entrypoint} failed:\n${err}\n${err.stack}`);
169
+ }
170
+ });
171
+ }
172
+ if (this.modules && Object.keys(this.modules).length == 0) {
173
+ let moduleEnd = false;
174
+ let moduleIterator = 0;
175
+ if (typeof this.target[this.entrypoint] != 'function' || !this.target[this.entrypoint]([], [], [moduleIterator])) {
176
+ throw Error('Unknown Webpack structure');
177
+ }
178
+ while (!moduleEnd) {
179
+ try {
180
+ this.modules[moduleIterator] = this.target[this.entrypoint]([], [], [moduleIterator]);
181
+ moduleIterator++;
182
+ }
183
+ catch (err) {
184
+ moduleEnd = true;
185
+ }
186
+ }
187
+ }
188
+ }
189
+ /**
190
+ * Method to hook into `window[this.entrypoint].push` adding a listener for new
191
+ * chunks being pushed into Webpack
192
+ *
193
+ * @example
194
+ * You can listen for newly pushed packages using the `moduleraid:webpack-push` event
195
+ * on `document`
196
+ *
197
+ * ```ts
198
+ * document.addEventListener('moduleraid:webpack-push', (e) => {
199
+ * // e.detail contains the arguments push() was called with
200
+ * console.log(e.detail)
201
+ * })
202
+ * ```
203
+ * @internal
204
+ */
205
+ setupPushEvent() {
206
+ const originalPush = this.target[this.entrypoint].push;
207
+ this.target[this.entrypoint].push = (...args) => {
208
+ const result = Reflect.apply(originalPush, this.target[this.entrypoint], args);
209
+ document.dispatchEvent(new CustomEvent('moduleraid:webpack-push', { detail: args }));
210
+ return result;
211
+ };
212
+ }
213
+ /**
214
+ * Method to try autodetecting a Webpack JSONP entrypoint based on common naming
215
+ *
216
+ * If the default entrypoint, or the entrypoint that's passed to the moduleRaid constructor
217
+ * already matches, the method exits early
218
+ *
219
+ * If `options.strict` has been set in the constructor and the initial entrypoint cannot
220
+ * be found, this method will error, demanding a strictly set entrypoint
221
+ * @internal
222
+ */
223
+ detectEntrypoint() {
224
+ if (this.target[this.entrypoint] != undefined) {
225
+ return;
226
+ }
227
+ if (this.strict) {
228
+ throw Error(`Strict mode is enabled and entrypoint at window.${this.entrypoint} couldn't be found. Please specify the correct one!`);
229
+ }
230
+ let windowObjects = Object.keys(this.target);
231
+ windowObjects = windowObjects
232
+ .filter((object) => object.toLowerCase().includes('chunk') || object.toLowerCase().includes('webpack'))
233
+ .filter((object) => typeof this.target[object] === 'function' || Array.isArray(this.target[object]));
234
+ if (windowObjects.length > 1) {
235
+ throw Error(`Multiple possible endpoints have been detected, please create a new moduleRaid instance with a specific one:\n${windowObjects.join(', ')}`);
236
+ }
237
+ if (windowObjects.length === 0) {
238
+ throw Error('No Webpack JSONP entrypoints could be detected');
239
+ }
240
+ this.log(`Entrypoint has been detected at window.${windowObjects[0]} and set for injection`);
241
+ this.entrypoint = windowObjects[0];
242
+ }
243
+ /**
244
+ * Recursive object-search function for modules
245
+ *
246
+ * @param object the object to search through
247
+ * @param query the query the object keys/values are searched for
248
+ * @returns boolean state of `object` containing `query` somewhere in it
249
+ * @internal
250
+ */
251
+ searchObject(object, query) {
252
+ for (const key in object) {
253
+ const value = object[key];
254
+ const lowerCaseQuery = query.toLowerCase();
255
+ if (typeof value != 'object') {
256
+ const lowerCaseKey = key.toString().toLowerCase();
257
+ if (lowerCaseKey.includes(lowerCaseQuery))
258
+ return true;
259
+ if (typeof value != 'object') {
260
+ const lowerCaseValue = value.toString().toLowerCase();
261
+ if (lowerCaseValue.includes(lowerCaseQuery))
262
+ return true;
263
+ }
264
+ else {
265
+ if (this.searchObject(value, query))
266
+ return true;
267
+ }
268
+ }
269
+ }
270
+ return false;
271
+ }
272
+ /**
273
+ * Method to search through the module object, searching for the fitting content
274
+ * if a string is supplied
275
+ *
276
+ * If query is supplied as a function, everything that returns true when passed
277
+ * to the query function will be returned
278
+ *
279
+ * @example
280
+ * With a string as query argument:
281
+ * ```ts
282
+ * const results = mR.findModule('feature')
283
+ * // => Array of module results
284
+ * ```
285
+ *
286
+ * With a function as query argument:
287
+ * ```ts
288
+ * const results = mR.findModule((module) => { typeof module === 'function' })
289
+ * // => Array of module results
290
+ * ```
291
+ *
292
+ * @param query query to search the module list for
293
+ * @return a list of modules fitting the query
294
+ */
295
+ findModule(query) {
296
+ const results = [];
297
+ const modules = Object.keys(this.modules);
298
+ if (modules.length === 0) {
299
+ throw new Error('There are no modules to search through!');
300
+ }
301
+ modules.forEach((key) => {
302
+ const module = this.modules[key.toString()];
303
+ if (module === undefined)
304
+ return;
305
+ try {
306
+ if (typeof query === 'string') {
307
+ query = query.toLowerCase();
308
+ switch (typeof module) {
309
+ case 'string':
310
+ if (module.toLowerCase().includes(query))
311
+ results.push(module);
312
+ break;
313
+ case 'function':
314
+ if (module.toString().toLowerCase().includes(query))
315
+ results.push(module);
316
+ break;
317
+ case 'object':
318
+ if (this.searchObject(module, query))
319
+ results.push(module);
320
+ break;
321
+ }
322
+ }
323
+ else if (typeof query === 'function') {
324
+ if (query(module))
325
+ results.push(module);
326
+ }
327
+ else {
328
+ throw new TypeError(`findModule can only find via string and function, ${typeof query} was passed`);
329
+ }
330
+ }
331
+ catch (err) {
332
+ this.log(`There was an error while searching through module '${key}':\n${err}\n${err.stack}`);
333
+ }
334
+ });
335
+ return results;
336
+ }
337
+ /**
338
+ * Method to search through the constructor array, searching for the fitting content
339
+ * if a string is supplied
340
+ *
341
+ * If query is supplied as a function, everything that returns true when passed
342
+ * to the query function will be returned
343
+ *
344
+ * @example
345
+ * With a string as query argument:
346
+ * ```ts
347
+ * const results = mR.findConstructor('feature')
348
+ * // => Array of constructor/module tuples
349
+ * ```
350
+ *
351
+ * With a function as query argument:
352
+ * ```ts
353
+ * const results = mR.findConstructor((constructor) => { constructor.prototype.value !== undefined })
354
+ * // => Array of constructor/module tuples
355
+ * ```
356
+ *
357
+ * Accessing the resulting data:
358
+ * ```ts
359
+ * // With array destructuring (ES6)
360
+ * const [constructor, module] = results[0]
361
+ *
362
+ * // ...or...
363
+ *
364
+ * // regular access
365
+ * const constructor = results[0][0]
366
+ * const module = results[0][1]
367
+ * ```
368
+ *
369
+ * @param query query to search the constructor list for
370
+ * @returns a list of constructor/module tuples fitting the query
371
+ */
372
+ findConstructor(query) {
373
+ const results = [];
374
+ const constructors = Object.keys(this.constructors);
375
+ if (constructors.length === 0) {
376
+ throw new Error('There are no constructors to search through!');
377
+ }
378
+ constructors.forEach((key) => {
379
+ const constructor = this.constructors[key];
380
+ try {
381
+ if (typeof query === 'string') {
382
+ query = query.toLowerCase();
383
+ if (constructor.toString().toLowerCase().includes(query))
384
+ results.push([this.constructors[key], this.modules[key]]);
385
+ }
386
+ else if (typeof query === 'function') {
387
+ if (query(constructor))
388
+ results.push([this.constructors[key], this.modules[key]]);
389
+ }
390
+ }
391
+ catch (err) {
392
+ this.log(`There was an error while searching through constructor '${key}':\n${err}\n${err.stack}`);
393
+ }
394
+ });
395
+ return results;
396
+ }
397
+ }