musescore-downloader/src/webpack-hook.ts

126 lines
3.4 KiB
TypeScript
Raw Normal View History

2020-10-21 14:58:24 +00:00
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2020-11-09 19:07:57 +00:00
import { hookNative } from './anti-detection'
2020-11-13 23:27:28 +00:00
const CHUNK_PUSH_FN = /^function [^r]\(\w\){/
2020-11-09 19:48:57 +00:00
2020-10-21 14:58:24 +00:00
interface Module {
(module, exports, __webpack_require__): void;
}
2020-11-18 18:57:56 +00:00
type WebpackJson = [(number | string)[], { [id: string]: Module }, any[]?][]
2020-10-21 14:58:24 +00:00
const moduleLookup = (id: string, globalWebpackJson: WebpackJson) => {
const pack = globalWebpackJson.find(x => x[1][id])!
return pack[1][id]
}
/**
* Retrieve (webpack_require) a module from the page's webpack package
*
* I know this is super hacky.
*/
export const webpackHook = (moduleId: string, moduleOverrides: { [id: string]: Module } = {}, globalWebpackJson: WebpackJson = window['webpackJsonpmusescore']) => {
const t = Object.assign((id: string, override = true) => {
const r: any = {}
const m: Module = (override && moduleOverrides[id])
? moduleOverrides[id]
: moduleLookup(id, globalWebpackJson)
m(r, r, t)
if (r.exports) return r.exports
return r
}, {
d (exp, name, fn) {
return Object.prototype.hasOwnProperty.call(exp, name) ||
Object.defineProperty(exp, name, { enumerable: true, get: fn })
},
n (e) {
2020-10-22 19:30:31 +00:00
const m = e.__esModule ? () => e.default : () => e
t.d(m, 'a', m)
return m
2020-10-21 14:58:24 +00:00
},
r (r) {
Object.defineProperty(r, '__esModule', { value: true })
},
e () {
return Promise.resolve()
},
})
return t(moduleId)
}
2020-11-12 18:28:40 +00:00
export const ALL = '*'
2020-11-17 17:00:41 +00:00
export const [webpackGlobalOverride, onPackLoad] = (() => {
type OnPackLoadFn = (pack: WebpackJson[0]) => any
2020-11-09 19:07:57 +00:00
const moduleOverrides: { [id: string]: Module } = {}
2020-11-17 17:00:41 +00:00
const onPackLoadFns: OnPackLoadFn[] = []
2020-11-09 19:07:57 +00:00
function applyOverride (pack: WebpackJson[0]) {
2020-11-12 18:28:40 +00:00
let entries = Object.entries(moduleOverrides)
// apply to all
const all = moduleOverrides[ALL]
if (all) {
entries = Object.keys(pack[1]).map(id => [id, all])
}
entries.forEach(([id, override]) => {
2020-11-09 19:07:57 +00:00
const mod = pack[1][id]
if (mod) {
pack[1][id] = function (n, r, t) {
// make exports configurable
t = Object.assign(t, {
d (exp, name, fn) {
return Object.defineProperty(exp, name, { enumerable: true, get: fn, configurable: true })
},
})
mod(n, r, t)
override(n, r, t)
}
}
})
}
// hook `webpackJsonpmusescore.push` as soon as `webpackJsonpmusescore` is available
2020-11-09 21:16:44 +00:00
let jsonp = window['webpackJsonpmusescore']
2020-11-09 19:48:57 +00:00
let hooked = false
2020-11-09 19:07:57 +00:00
Object.defineProperty(window, 'webpackJsonpmusescore', {
get () { return jsonp },
set (v: WebpackJson) {
jsonp = v
2020-11-13 23:27:28 +00:00
if (!hooked && v.push.toString().match(CHUNK_PUSH_FN)) {
2020-11-09 19:48:57 +00:00
hooked = true
hookNative(v, 'push', (_fn) => {
return function (pack) {
2020-11-17 17:00:41 +00:00
onPackLoadFns.forEach(fn => fn(pack))
2020-11-09 19:48:57 +00:00
applyOverride(pack)
return _fn.call(this, pack)
}
})
}
2020-11-09 19:07:57 +00:00
},
})
2020-11-17 17:00:41 +00:00
return [
// set overrides
(moduleId: string, override: Module) => {
moduleOverrides[moduleId] = override
},
// set onPackLoad listeners
(fn: OnPackLoadFn) => {
onPackLoadFns.push(fn)
},
] as const
2020-11-09 19:07:57 +00:00
})()
2020-11-17 17:09:23 +00:00
export const webpackContext = new Promise<any>((resolve) => {
webpackGlobalOverride(ALL, (n, r, t) => {
resolve(t)
})
})
2020-10-21 14:58:24 +00:00
export default webpackHook