···74 }
75 }
76000000077 for (const [id, func] of Object.entries(entry)) {
78 if (func.__moonlight === true) continue;
79- let moduleString = Object.hasOwn(moduleCache, id)
80- ? moduleCache[id]
81- : func.toString().replace(/\n/g, "");
8283 for (let i = 0; i < patches.length; i++) {
84 const patch = patches[i];
···151 }
152 }
15300154 try {
155- let parsed = moonlight.lunast.parseScript(id, `(\n${moduleString}\n)`);
156 if (parsed != null) {
157- // parseScript adds an extra ; for some reason
158- parsed = parsed.trimEnd().substring(0, parsed.lastIndexOf(";"));
159- if (patchModule(id, "lunast", parsed)) {
160- moduleString = parsed;
00000161 }
162 }
163 } catch (e) {
···170 !entry[id].__moonlight
171 ) {
172 const wrapped =
173- `(${moduleString}).apply(this, arguments)\n` +
174 `//# sourceURL=Webpack-Module-${id}`;
175 entry[id] = new Function(
176 "module",
···181 entry[id].__moonlight = true;
182 }
183 }
184-185- moduleCache[id] = moduleString;
186 }
187}
188···302*/
303export async function installWebpackPatcher() {
304 await handleModuleDependencies();
0000305306 let realWebpackJsonp: WebpackJsonp | null = null;
307 Object.defineProperty(window, "webpackChunkdiscord_app", {
···74 }
75 }
7677+ // Populate the module cache
78+ for (const [id, func] of Object.entries(entry)) {
79+ if (!Object.hasOwn(moduleCache, id) && func.__moonlight !== true) {
80+ moduleCache[id] = func.toString().replace(/\n/g, "");
81+ }
82+ }
83+84 for (const [id, func] of Object.entries(entry)) {
85 if (func.__moonlight === true) continue;
86+ let moduleString = moduleCache[id];
008788 for (let i = 0; i < patches.length; i++) {
89 const patch = patches[i];
···156 }
157 }
158159+ moduleCache[id] = moduleString;
160+161 try {
162+ const parsed = moonlight.lunast.parseScript(id, moduleString);
163 if (parsed != null) {
164+ for (const [parsedId, parsedScript] of Object.entries(parsed)) {
165+ // parseScript adds an extra ; for some reason
166+ const fixedScript = parsedScript
167+ .trimEnd()
168+ .substring(0, parsedScript.lastIndexOf(";"));
169+170+ if (patchModule(parsedId, "lunast", fixedScript)) {
171+ moduleCache[parsedId] = fixedScript;
172+ }
173 }
174 }
175 } catch (e) {
···182 !entry[id].__moonlight
183 ) {
184 const wrapped =
185+ `(${moduleCache[id]}).apply(this, arguments)\n` +
186 `//# sourceURL=Webpack-Module-${id}`;
187 entry[id] = new Function(
188 "module",
···193 entry[id].__moonlight = true;
194 }
195 }
00196 }
197}
198···312*/
313export async function installWebpackPatcher() {
314 await handleModuleDependencies();
315+316+ moonlight.lunast.setModuleSourceGetter((id) => {
317+ return moduleCache[id] ?? null;
318+ });
319320 let realWebpackJsonp: WebpackJsonp | null = null;
321 Object.defineProperty(window, "webpackChunkdiscord_app", {
+1-1
packages/lunast/README.md
···114115Not really. LunAST runs in roughly ~10ms on [my](https://github.com/NotNite) machine, with filtering for what modules to parse. Parsing every module takes only a second. There are future plans to cache and parallelize the process, so that load times are only slow once.
116117-You can measure how long LunAST took to process with the `moonlight.lunast.elapsed` variable
118119### Does this mean patches are dead?
120
···114115Not really. LunAST runs in roughly ~10ms on [my](https://github.com/NotNite) machine, with filtering for what modules to parse. Parsing every module takes only a second. There are future plans to cache and parallelize the process, so that load times are only slow once.
116117+You can measure how long LunAST took to process with the `moonlight.lunast.elapsed` variable.
118119### Does this mean patches are dead?
120
···16 >;
17 private processors: Processor[];
18 private defaultRequire?: (id: string) => any;
19+ private getModuleSource?: (id: string) => string;
2021 elapsed: number;
22···38 return "dev";
39 }
4041+ public parseScript(id: string, code: string): Record<string, string> {
42 const start = performance.now();
4344 const available = [...this.processors]
45 .sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0))
46+ .filter((x) => {
47+ if (x.find == null) return true;
48+ const finds = Array.isArray(x.find) ? x.find : [x.find];
49+ return finds.every((find) =>
50+ typeof find === "string" ? code.indexOf(find) !== -1 : find.test(code)
51+ );
52+ })
53+ .filter((x) => x.manual !== true);
54+55+ const ret = this.parseScriptInternal(id, code, available);
56+57+ const end = performance.now();
58+ this.elapsed += end - start;
59+60+ return ret;
61+ }
62+63+ // This is like this so processors can trigger other processors while they're parsing
64+ private parseScriptInternal(
65+ id: string,
66+ code: string,
67+ processors: Processor[]
68+ ) {
69+ const ret: Record<string, string> = {};
70+ if (processors.length === 0) return ret;
7172+ // Wrap so the anonymous function is valid JS
73+ const module = parseFixed(`(\n${code}\n)`);
74 let dirty = false;
75 const state: ProcessorState = {
76 id,
···78 lunast: this,
79 markDirty: () => {
80 dirty = true;
81+ },
82+ trigger: (id, tag) => {
83+ const source = this.getModuleSourceById(id);
84+ if (source == null) return;
85+ if (this.successful.has(tag)) return;
86+ const processor = this.processors.find((x) => x.name === tag);
87+ if (processor == null) return;
88+ const theirRet = this.parseScriptInternal(id, source, [processor]);
89+ Object.assign(ret, theirRet);
90 }
91 };
9293+ for (const processor of processors) {
94 if (processor.process(state)) {
95 this.processors.splice(this.processors.indexOf(processor), 1);
96 this.successful.add(processor.name);
···98 }
99100 const str = dirty ? generate(module) : null;
101+ if (str != null) ret[id] = str;
00102103+ return ret;
104 }
105106 public getType(name: string) {
···161 // TODO: call this with require we obtain from the webpack entrypoint
162 public setDefaultRequire(require: (id: string) => any) {
163 this.defaultRequire = require;
164+ }
165+166+ public setModuleSourceGetter(getSource: (id: string) => string) {
167+ this.getModuleSource = getSource;
168+ }
169+170+ public getModuleSourceById(id: string) {
171+ return this.getModuleSource?.(id) ?? null;
172 }
173174 public remap<Id extends keyof Remapped>(
+52-5
packages/lunast/src/modules/test.ts
···1import { traverse, is } from "estree-toolkit";
2-import { getPropertyGetters, register, magicAST } from "../utils";
3import { BlockStatement } from "estree-toolkit/dist/generated/types";
45// These aren't actual modules yet, I'm just using this as a testbed for stuff
···44 "balls"
45)`)!;
46 for (const data of Object.values(getters)) {
47- if (!is.identifier(data.argument)) continue;
4849- const node = data.scope.getOwnBinding(data.argument.name);
50 if (!node) continue;
5152 const body = node.path.get<BlockStatement>("body");
···67 const fields = [];
6869 for (const [name, data] of Object.entries(getters)) {
70- if (!is.identifier(data.argument)) continue;
71- const node = data.scope.getOwnBinding(data.argument.name);
72 if (!node) continue;
7374 let isSupportsCopy = false;
···121 return false;
122 }
123});*/
00000000000000000000000000000000000000000000000
···1import { traverse, is } from "estree-toolkit";
2+import { getPropertyGetters, register, magicAST, getImports } from "../utils";
3import { BlockStatement } from "estree-toolkit/dist/generated/types";
45// These aren't actual modules yet, I'm just using this as a testbed for stuff
···44 "balls"
45)`)!;
46 for (const data of Object.values(getters)) {
47+ if (!is.identifier(data.expression)) continue;
4849+ const node = data.scope.getOwnBinding(data.expression.name);
50 if (!node) continue;
5152 const body = node.path.get<BlockStatement>("body");
···67 const fields = [];
6869 for (const [name, data] of Object.entries(getters)) {
70+ if (!is.identifier(data.expression)) continue;
71+ const node = data.scope.getOwnBinding(data.expression.name);
72 if (!node) continue;
7374 let isSupportsCopy = false;
···121 return false;
122 }
123});*/
124+125+// Triggering a processor from another processor
126+register({
127+ name: "FluxDispatcherParent",
128+ find: ["isDispatching", "dispatch", "googlebot"],
129+ process({ id, ast, lunast, trigger }) {
130+ const imports = getImports(ast);
131+ // This is so stupid lol
132+ const usages = Object.entries(imports)
133+ .map(([name, data]): [string, number] => {
134+ if (!is.identifier(data.expression)) return [name, 0];
135+ const binding = data.scope.getOwnBinding(data.expression.name);
136+ if (!binding) return [name, 0];
137+ return [name, binding.references.length];
138+ })
139+ .sort(([, a], [, b]) => b! - a!)
140+ .map(([name]) => name);
141+142+ const dispatcher = usages[1].toString();
143+ trigger(dispatcher, "FluxDispatcher");
144+ return true;
145+ }
146+});
147+148+register({
149+ name: "FluxDispatcher",
150+ manual: true,
151+ process({ id, ast, lunast }) {
152+ lunast.addModule({
153+ name: "FluxDispatcher",
154+ id,
155+ type: "FluxDispatcher"
156+ });
157+158+ lunast.addType({
159+ name: "FluxDispatcher",
160+ fields: [
161+ {
162+ name: "default",
163+ unmapped: "Z"
164+ }
165+ ]
166+ });
167+168+ return true;
169+ }
170+});