diff --git a/workspaces/leetcode-zen-mode/src/extension/injectJsonScriptMiddleware.ts b/workspaces/leetcode-zen-mode/src/extension/injectJsonScriptMiddleware.ts new file mode 100644 index 00000000..2d0f34e8 --- /dev/null +++ b/workspaces/leetcode-zen-mode/src/extension/injectJsonScriptMiddleware.ts @@ -0,0 +1,53 @@ +import invariant from "invariant"; +import type { JsonValue } from "type-fest"; + +import { jsonParseSafe } from "@code-chronicles/util/jsonParseSafe"; + +type ProxiedPropertyKey = "innerHTML" | "innerText" | "textContent"; + +type MiddlewareFn = ( + data: JsonValue, + script: HTMLScriptElement, + property: ProxiedPropertyKey, +) => JsonValue; + +function inject( + proto: TProto, + property: ProxiedPropertyKey, + middlewareFn: MiddlewareFn, +): void { + const prevDescriptor = Object.getOwnPropertyDescriptor(proto, property); + + invariant( + prevDescriptor && prevDescriptor.get, + `\`${proto.constructor.name}.prototype.${property}\` property descriptor didn't have the expected form!`, + ); + const prevDescriptorGet = prevDescriptor.get; + + Object.defineProperty(HTMLScriptElement.prototype, property, { + ...prevDescriptor, + get(this: HTMLScriptElement) { + const data = prevDescriptorGet.call(this); + + // If the data doesn't parse as JSON, we pass it through unchanged. + // If it does parse as JSON, we'll run the middleware. + const parsedData = jsonParseSafe(data); + if (parsedData) { + return JSON.stringify(middlewareFn(parsedData.data, this, property)); + } + + return data; + }, + }); +} + +/** + * Injects a function to process and possibly replace JSON data stored within + *