diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 200aa3f83..d3068492f 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -343,7 +343,7 @@ async function runtime(token: string) { // True if resolved, false otherwise const chunksSearchPromises = [] as Array<() => boolean>; - const LazyChunkRegex = canonicalizeMatch(/(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\)))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); + const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/g); async function searchAndLoadLazyChunks(factoryCode: string) { const lazyChunks = factoryCode.matchAll(LazyChunkRegex); @@ -353,8 +353,7 @@ async function runtime(token: string) { // the chunk containing the component const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT"); - await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIdsArray, rawChunkIdsSingle, entryPoint]) => { - const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle; + await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => { const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Vencord.Webpack.ChunkIdsRegex)).map(m => m[1]) : []; if (chunkIds.length === 0) { @@ -525,14 +524,14 @@ async function runtime(token: string) { } else if (method === "extractAndLoadChunks") { const [code, matcher] = args; - const module = Vencord.Webpack.findModuleFactory(...code); - if (module) result = module.toString().match(canonicalizeMatch(matcher)); + result = await Vencord.Webpack.extractAndLoadChunks(code, matcher); + if (result === false) result = null; } else { // @ts-ignore result = Vencord.Webpack[method](...args); } - if (result == null || ("$$vencordInternal" in result && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; + if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; } catch (e) { let logMessage = searchType; if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`; diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index f2e6e58a8..9e0c0d000 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -402,14 +402,14 @@ export function findExportedComponentLazy(...props: stri }); } -export const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\(\[(\i\.\i\("[^)]+?"\)[^\]]+?)\]\)|(\i\.\i\("[^)]+?"\))|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/; -export const ChunkIdsRegex = /\("(.+?)"\)/g; +export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/; +export const ChunkIdsRegex = /\("([^"]+?)"\)/g; /** * Extract and load chunks using their entry point * @param code An array of all the code the module factory containing the lazy chunk loading must include - * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory - * @returns A promise that resolves when the chunks were loaded + * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the first lazy chunk loading found in the module factory + * @returns A promise that resolves with a boolean whether the chunks were loaded */ export async function extractAndLoadChunks(code: string[], matcher: RegExp = DefaultExtractAndLoadChunksRegex) { const module = findModuleFactory(...code); @@ -417,7 +417,11 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def const err = new Error("extractAndLoadChunks: Couldn't find module factory"); logger.warn(err, "Code:", code, "Matcher:", matcher); - return; + // Strict behaviour in DevBuilds to fail early and make sure the issue is found + if (IS_DEV && !devToolsOpen) + throw err; + + return false; } const match = module.toString().match(canonicalizeMatch(matcher)); @@ -429,10 +433,10 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def if (IS_DEV && !devToolsOpen) throw err; - return; + return false; } - const [, rawChunkIdsArray, rawChunkIdsSingle, entryPointId] = match; + const [, rawChunkIds, entryPointId] = match; if (Number.isNaN(Number(entryPointId))) { const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number"); logger.warn(err, "Code:", code, "Matcher:", matcher); @@ -441,16 +445,27 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def if (IS_DEV && !devToolsOpen) throw err; - return; + return false; } - const rawChunkIds = rawChunkIdsArray ?? rawChunkIdsSingle; if (rawChunkIds) { const chunkIds = Array.from(rawChunkIds.matchAll(ChunkIdsRegex)).map((m: any) => m[1]); await Promise.all(chunkIds.map(id => wreq.e(id))); } + if (wreq.m[entryPointId] == null) { + const err = new Error("extractAndLoadChunks: Entry point is not loaded in the module factories, perhaps one of the chunks failed to load"); + logger.warn(err, "Code:", code, "Matcher:", matcher); + + // Strict behaviour in DevBuilds to fail early and make sure the issue is found + if (IS_DEV && !devToolsOpen) + throw err; + + return false; + } + wreq(entryPointId); + return true; } /** @@ -458,8 +473,8 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def * * Extract and load chunks using their entry point * @param code An array of all the code the module factory containing the lazy chunk loading must include - * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory - * @returns A function that returns a promise that resolves when the chunks were loaded, on first call + * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the first lazy chunk loading found in the module factory + * @returns A function that returns a promise that resolves with a boolean whether the chunks were loaded, on first call */ export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) { if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]);