-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor whew, add autodetection support
- Loading branch information
Showing
8 changed files
with
363 additions
and
265 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
import matchModule from "./matchModule"; | ||
import { getWpToolsFunc } from "./wpTools"; | ||
|
||
export default class Patcher { | ||
constructor(config) { | ||
this.name = config.name; | ||
this.chunkObject = config.chunkObject; | ||
this.webpackVersion = config.webpackVersion; | ||
this.inspectAll = config.inspectAll; | ||
|
||
this.modules = new Set(config.modules ?? []); | ||
this.patches = new Set(config.patches ?? []); | ||
|
||
// Validation | ||
if (typeof this.webpackVersion == "number") { | ||
this.webpackVersion = this.webpackVersion.toString(); | ||
} | ||
|
||
// Populate patches to apply and modules to inject | ||
this.patchesToApply = new Set(); | ||
if (this.patches) { | ||
for (const patch of this.patches) { | ||
this.patchesToApply.add(patch); | ||
} | ||
} | ||
|
||
this.modulesToInject = new Set(); | ||
if (this.modules) { | ||
for (const module of this.modules) { | ||
if (module.needs != undefined && module.needs instanceof Array) { | ||
module.needs = new Set(module.needs); | ||
} | ||
this.modulesToInject.add(module); | ||
} | ||
} | ||
|
||
if (config.injectWpTools) { | ||
this.modulesToInject.add({ | ||
name: "wpTools", | ||
// This is sorta a scope hack. | ||
// If we rewrap this function, it will lose its scope (in this case the match module import and the chunk object name) | ||
run: getWpToolsFunc(this.chunkObject), | ||
entry: true, | ||
}); | ||
} | ||
} | ||
|
||
run() { | ||
if (this.webpackVersion == "4" || this.webpackVersion == "5") { | ||
this._interceptWebpackModern(); | ||
} else { | ||
this._interceptWebpackLegacy; | ||
} | ||
} | ||
|
||
_interceptWebpackModern() { | ||
// This is necesary since some sites (twitter) define the chunk object earlier | ||
let realChunkObject = window[this.chunkObject]; | ||
const patcher = this; | ||
|
||
Object.defineProperty(window, this.chunkObject, { | ||
set: function set(value) { | ||
realChunkObject = value; | ||
// Don't infinitely re-wrap .push() | ||
// Every webpack chunk reassigns the chunk array, triggering the setter every time | ||
// `(self.webpackChunk = self.webpackChunk || [])` | ||
if (!value.push.__wpt_injected) { | ||
realChunkObject = value; | ||
const realPush = value.push; | ||
|
||
value.push = function (chunk) { | ||
// This is necesary because webpack will re-wrap the .push function | ||
// Without this check, we'll patch modules multiple times | ||
if (!chunk.__wpt_processed) { | ||
chunk.__wpt_processed = true; | ||
patcher._patchModules(chunk[1]); | ||
patcher._injectModules(chunk); | ||
} | ||
return realPush.apply(this, arguments); | ||
}; | ||
|
||
value.push.__wpt_injected = true; | ||
if (realPush == Array.prototype.push) { | ||
console.log("[wpTools] Injected " + patcher._chunkObject + " (before webpack runtime)"); | ||
} else { | ||
console.log("[wpTools] Injected " + patcher._chunkObject + " (at webpack runtime)"); | ||
} | ||
} | ||
}, | ||
get: function get() { | ||
return realChunkObject; | ||
}, | ||
configurable: true, | ||
}); | ||
} | ||
|
||
_interceptWebpackLegacy() {} | ||
|
||
_patchModules(modules) { | ||
for (const id in modules) { | ||
if (modules[id].__wpt_processed) { | ||
continue; | ||
} | ||
let funcStr = Function.prototype.toString.apply(modules[id]); | ||
|
||
const matchingPatches = []; | ||
for (const patch of this.patchesToApply) { | ||
if (matchModule(funcStr, patch.find)) { | ||
matchingPatches.push(patch); | ||
this.patchesToApply.delete(patch); | ||
} | ||
} | ||
|
||
for (const patch of matchingPatches) { | ||
funcStr = funcStr.replace(patch.replace.match, patch.replace.replacement); | ||
} | ||
|
||
if (matchingPatches.length > 0 || this.inspectAll) { | ||
let debugString = ""; | ||
if (matchingPatches.length > 0) { | ||
debugString += "Patched by: " + matchingPatches.map((patch) => patch.name).join(", "); | ||
} | ||
|
||
modules[id] = new Function( | ||
"module", | ||
"exports", | ||
"webpackRequire", | ||
`(${funcStr}).apply(this, arguments)\n// ${debugString}\n//# sourceURL=${this.chunkObject}-Module-${id}`, | ||
); | ||
modules[id].__wpt_patched = true; | ||
} | ||
|
||
modules[id].__wpt_funcStr = funcStr; | ||
modules[id].__wpt_processed = true; | ||
} | ||
} | ||
|
||
_injectModules(chunk) { | ||
const readyModules = new Set(); | ||
|
||
for (const moduleToInject of this.modulesToInject) { | ||
if (moduleToInject?.needs?.size > 0) { | ||
for (const need of moduleToInject.needs) { | ||
for (const wpModule of Object.entries(chunk[1])) { | ||
// match { moduleId: "id" } as well as strings and regex | ||
if ((need?.moduleId && wpModule[0] == need.moduleId) || matchModule(wpModule[1].__wpt_funcStr, need)) { | ||
moduleToInject.needs.delete(need); | ||
if (moduleToInject.needs.size == 0) { | ||
readyModules.add(moduleToInject); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
} else { | ||
readyModules.add(moduleToInject); | ||
} | ||
} | ||
|
||
if (readyModules.size > 0) { | ||
const injectModules = {}; | ||
const injectEntries = []; | ||
|
||
for (const readyModule of readyModules) { | ||
this.modulesToInject.delete(readyModule); | ||
injectModules[readyModule.name] = readyModule.run; | ||
if (readyModule.entry) { | ||
injectEntries.push(readyModule.name); | ||
} | ||
} | ||
|
||
// Convert array to object for named modules | ||
if (chunk[1] instanceof Array) { | ||
const origChunkArray = chunk[1]; | ||
chunk[1] = {}; | ||
origChunkArray.forEach((module, index) => { | ||
chunk[1][index] = module; | ||
}); | ||
} | ||
|
||
// merge our modules with original modules | ||
chunk[1] = Object.assign(chunk[1], injectModules); | ||
|
||
if (injectEntries.length > 0) { | ||
switch (this.webpackVersion) { | ||
case "5": | ||
if (chunk[2]) { | ||
const originalEntry = chunk[2]; | ||
chunk[2] = function (webpackRequire) { | ||
originalEntry.apply(this, arguments); | ||
injectEntries.forEach(webpackRequire); | ||
}; | ||
} else { | ||
chunk[2] = function (webpackRequire) { | ||
injectEntries.forEach(webpackRequire); | ||
}; | ||
} | ||
break; | ||
case "4": | ||
if (chunk[2]?.[0]) { | ||
chunk[2]?.[0].concat([injectEntries]); | ||
} else { | ||
chunk[2] = [injectEntries]; | ||
} | ||
break; | ||
} | ||
} | ||
console.log(chunk); | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,25 @@ | ||
import config from "./config"; | ||
import { interceptWebpack } from "./patcher"; | ||
|
||
import Patcher from "./Patcher"; | ||
import { injectEverywhere } from "./injectEverywhere"; | ||
|
||
|
||
export const globalConfig = window.__webpackTools_config; | ||
delete window.__webpackTools_config; | ||
|
||
export const siteConfigs = new Set(); | ||
for (let siteConfig of globalConfig.siteConfigs) { | ||
if (siteConfig.matchSites?.includes(window.location.host)) { | ||
siteConfigs.add(siteConfig); | ||
break; | ||
} | ||
} | ||
|
||
// todo: magicrequire everywhere impl | ||
if (config) { | ||
interceptWebpack(); | ||
if (siteConfigs.size > 0) { | ||
for (const siteConfig of siteConfigs) { | ||
const patcher = new Patcher(siteConfig); | ||
patcher.run(); | ||
} | ||
} else if (globalConfig.wpToolsEverywhere) { | ||
window.addEventListener("load", injectEverywhere); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { getWpToolsFunc } from "./wpTools"; | ||
|
||
function getVersion(chunkObject) { | ||
if (chunkObject instanceof Array) { | ||
return "modern"; | ||
} else { | ||
return "legacy"; | ||
} | ||
} | ||
|
||
function injectWpTools(chunkObjectName) { | ||
const chunkObject = window[chunkObjectName]; | ||
|
||
if (chunkObject.__wpt_everywhere_injected) { | ||
return; | ||
} | ||
const version = getVersion(chunkObject); | ||
|
||
console.log("[wpTools] Detected " + chunkObjectName + " using webpack " + version); | ||
|
||
switch (version) { | ||
case "modern": | ||
// Gross Hack to support both webpack 4 and webpack 5 | ||
var load = function (webpackRequire) { | ||
webpackRequire("wpTools"); | ||
}; | ||
load[0] = ["wpTools"]; | ||
load[Symbol.iterator] = function () { | ||
return { | ||
read: false, | ||
next() { | ||
if (!this.read) { | ||
this.read = true; | ||
return { done: false, value: 0 }; | ||
} else { | ||
return { done: true }; | ||
} | ||
}, | ||
}; | ||
}; | ||
chunkObject.__wpt_everywhere_injected = true | ||
chunkObject.push([["wpTools"], { wpTools: getWpToolsFunc(chunkObjectName) }, load]); | ||
break; | ||
} | ||
} | ||
|
||
export function injectEverywhere() { | ||
for (const key of Object.getOwnPropertyNames(window)) { | ||
if ((key.includes("webpackJsonp") || key.includes("webpackChunk") || key.includes("__LOADABLE_LOADED_CHUNKS__")) && !key.startsWith("wpTools")) { | ||
injectWpTools(key); | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export default function matchModule(moduleStr, queryArg) { | ||
const queryArray = queryArg instanceof Array ? queryArg : [queryArg]; | ||
return queryArray.some((query) => { | ||
// we like our microoptimizations https://jsben.ch/Zk8aw | ||
if (query instanceof RegExp) { | ||
return query.test(moduleStr); | ||
} else { | ||
return moduleStr.includes(query); | ||
} | ||
}); | ||
} |
Oops, something went wrong.