Skip to content

Commit

Permalink
prevent patching modules multiple times
Browse files Browse the repository at this point in the history
  • Loading branch information
adryd325 committed Nov 23, 2023
1 parent 3593d3b commit ba699dd
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 16 deletions.
5 changes: 4 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
- [ ] webpack 3 support for everything?
- [ ] glob matching for site names (for examle: \*.twitter.com) in userscript part
- [ ] config validation with descriptive errors
- [ ] rework configs
- [x] rework configs
- [ ] check if Function.prototype.toString() is faster than checking for \_\_wpt_funcStr
- [ ] wpTools/findByExports: recurse into objects when searching? getters could pose a problem however
- [ ] add obfuscated code helpers and swc helpers in wpTools
- [ ] find a better way to do userscripts, ideally not fetching remotely
- [ ] log errors while patching (parse, patches that dont fire, etc)
- [ ] actually good documentation and tutorials
6 changes: 3 additions & 3 deletions dist/webpackTools.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@
matchSites: ["twitter.com"], // String or Array of strings of sites to inject on. Matches globs (eg. *.discord.com)
chunkObject: "webpackChunk_twitter_responsive_web", // Name of webpack chunk object to intercept
webpackVersion: "5", // Version of webpack used to compile. TODO: Document this. Supported are 4 and 5
inspectAll: true, // Whether to isolate every module. Allows for viewing an individual module in devtools without the whole rest of the chunk
inspectAll: true, // Whether to isolate every module. Allows for viewing an individual module in devtools without the whole rest of the chunk, but has a huge performance impact
patches: [
{
name: "patchingDemo", // Used for debugging purposes, logging if a patch fails (TODO) and a comment of which patches affected a module
find: "(window.__INITIAL_STATE__", // String, regexp or an array of them to match a module we're patching. Best to keep this a single string if possible for performance reasons
replace: {
// match and replace are literally passed to `String.prototype.replace(match, replacement)`
match: /const .{1,3}=.\..\(window\.__INITIAL_STATE__/,
match: /(const|var) .{1,3}=.\..\(window\.__INITIAL_STATE__/,
replacement: (orig) => `console.log('Patches work!!!');${orig}`,
},
},
],
modules: [
{
name: "modulesDemo", // name the function will use when injected, required
name: "modulesDemo", // Id of the module being injected (required)
needs: new Set(), // set of strings, or regexes of modules that need to be loaded before injecting this one. can also be `{moduleId: <moduleId>}` if depending on other injected or named modules
entry: true, // if true, the module will evaluate immediately uppon injection. otherwise it will not evaluate until it's require()'d by another module
run: function (module, exports, webpackRequire) { // the actual webpack module.
Expand Down
27 changes: 19 additions & 8 deletions src/patcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,31 @@ export function interceptWebpack() {

Object.defineProperty(window, chunkObjectName, {
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 webpackPush = value.push;
const realPush = value.push;

value.push = function (chunk) {
if (!webpackPush.__wpt_injected) {
// 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;
patchModules(chunk[1]);
injectModules(chunk);
}
return webpackPush.apply(this, arguments);
return realPush.apply(this, arguments);
};

value.push.__wpt_injected = true;
console.log("injected " + chunkObjectName);
if (realPush == Array.prototype.push) {
console.log("Injected " + chunkObjectName + " (before webpack runtime)")
} else {
console.log("Injected " + chunkObjectName + " (at webpack runtime)")
}
}
},
get: function get() {
Expand Down Expand Up @@ -51,7 +61,8 @@ if (config.patches) {
patchesToApply.add(patch);
}
}
export function patchModules(modules) {

function patchModules(modules) {
for (const id in modules) {
let funcStr = Function.prototype.toString.apply(modules[id]);

Expand Down Expand Up @@ -96,11 +107,13 @@ if (config.modules) {

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)
run: wpTools,
entry: true,
});

export function injectModules(chunk) {
function injectModules(chunk) {
const readyModules = new Set();

for (const moduleToInject of modulesToInject) {
Expand Down Expand Up @@ -134,8 +147,6 @@ export function injectModules(chunk) {
}
}

// Patch our own modules, for fun :)
patchModules(injectModules);
chunk[1] = Object.assign(chunk[1], injectModules);
if (injectEntries.length > 0) {
switch (config.webpackVersion) {
Expand Down
5 changes: 1 addition & 4 deletions src/wpTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ export default function wpTools(module, exports, webpackRequire) {
});
})
.map(([moduleId, exportCache]) => {
return {
id: moduleId,
exports: exportCache,
};
return exportCache;
});
}

Expand Down

0 comments on commit ba699dd

Please sign in to comment.