-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathNext.js v13+ - Extract Chunk URLs.js
More file actions
111 lines (98 loc) · 4.05 KB
/
Next.js v13+ - Extract Chunk URLs.js
File metadata and controls
111 lines (98 loc) · 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Next.js v13+ - Extract Chunk URLs.js
// https://github.com/0xdevalias/userscripts/tree/main/devtools-snippets
//
// See also:
// - https://gist.github.com/search?q=user:0xdevalias+webpack
// - https://gist.github.com/0xdevalias/ac465fb2f7e6fded183c2a4273d21e61#react-server-components-nextjs-v13-and-webpack-notes-on-streaming-wire-format-__next_f-etc
const parseJSONFromEntry = entry => {
const jsonPart = entry.substring(entry.indexOf('[') + 1, entry.lastIndexOf(']'));
try {
return JSON.parse(`[${jsonPart}]`);
} catch (e) {
console.error("Failed to parse JSON for entry: ", entry);
return []; // Return an empty array or null as per error handling choice
}
};
// Function to transform dependencies into a simpler, directly accessible format
function transformDependencies(dependencies) {
return Object.values(dependencies).reduce((acc, currentDeps) => {
Object.entries(currentDeps).forEach(([moduleId, path]) => {
// If the paths match, skip to the next entry
if (acc?.[moduleId] === path) return
if (!acc[moduleId]) {
// If this module ID has not been encountered yet, initialize it with the current path
acc[moduleId] = path;
} else if (typeof acc[moduleId] === 'string' && acc[moduleId] !== path) {
// If the current path for this module ID is different from the existing one,
// and the existing one is a string, transform it into an array containing both paths.
const oldPath = acc[moduleId];
acc[moduleId] = [oldPath, path];
} else if (Array.isArray(acc[moduleId]) && !acc[moduleId].includes(path)) {
// If the existing entry for this module ID is an array and does not already include the current path,
// add the current path to the array.
acc[moduleId].push(path);
} else {
// Log any unhandled cases for further investigation. This could be used to catch any unexpected data structures or duplicates.
console.error('Unhandled case', { acc, currentDeps, moduleId, path });
}
});
return acc;
}, {});
}
// Get _next script urls
const scriptTags = document.querySelectorAll('html script[src*="_next"]');
const scriptUrls = Array.from(scriptTags).map(tag => tag.src).sort()
// console.log(scriptUrls);
// Get imports/etc (v3)
const moduleDependencies =
self.__next_f
.map(f => f?.[1])
.filter(f => f?.includes('static/'))
.flatMap(f => f.split('\n'))
.map(parseJSONFromEntry)
.filter(f => Array.isArray(f) ? f.length > 0 : !!f)
.map(f => {
if (!Array.isArray(f?.[1])) { return f } else {
// Convert alternating key/value array to an object
const keyValueArray = f[1];
const keyValuePairs = [];
for (let i = 0; i < keyValueArray.length; i += 2) {
keyValuePairs.push([keyValueArray[i], keyValueArray[i + 1]]);
}
f[1] = Object.fromEntries(keyValuePairs);
return f;
}
})
.filter(f => Array.isArray(f) && f.length === 3 && typeof f?.[1] === 'object')
.reduce((acc, [moduleId, dependencies, _]) => {
acc[moduleId] = dependencies;
return acc;
}, {});
const chunkMappings = transformDependencies(moduleDependencies)
const uniqueChunkPaths = Array.from(new Set(Object.values(chunkMappings))).sort()
const dynamicChunkUrls = uniqueChunkPaths
.map(path => `https://www.udio.com/_next/${path}`)
.sort()
const chunkUrls = Array.from(new Set([...scriptUrls, ...dynamicChunkUrls])).sort()
const chunkUrlsJoined = chunkUrls.join('\n')
const buildId = self.__next_f
.map(f => f?.[1])
.filter(f => f?.includes('buildId'))
.flatMap(f => f.trim().split('\n'))
.flatMap(parseJSONFromEntry)
.map(f => Array.isArray(f) ? f.flat() : f)
.map(f => f?.[3]?.buildId)
.filter(Boolean)?.[0]
const chunkUrlsJoinedWithBuildId = `${chunkUrlsJoined}\n${buildId}`
console.log({
scriptUrls,
moduleDependencies,
chunkMappings,
uniqueChunkPaths,
dynamicChunkUrls,
chunkUrls,
buildId,
})
console.log(chunkUrlsJoinedWithBuildId)
copy(chunkUrlsJoinedWithBuildId)
console.log('Chunk URLs (joined) + BuildID copied to clipboard')