-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackground.js
More file actions
369 lines (321 loc) · 10.9 KB
/
background.js
File metadata and controls
369 lines (321 loc) · 10.9 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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
// @charset "UTF-8";
console.log('Background script loaded');
// 使用importScripts直接加载JSZip
try {
console.log('Loading JSZip');
self.importScripts('jszip.min.js');
console.log('JSZip loaded successfully');
} catch (e) {
console.error('Failed to load JSZip:', e);
}
// 设置默认图标
chrome.runtime.onInstalled.addListener(() => {
console.log('Extension installed');
updateBadge('?');
});
// 监听标签页更新,重置图标
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete') {
updateBadge('?', tabId);
}
});
// 监听消息
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
console.log('Background received message:', message.type, sender?.tab?.id);
if (message.type === 'createZip') {
console.log('Creating ZIP with', Object.keys(message.files).length, 'files');
// 检查JSZip是否已加载
if (typeof JSZip === 'undefined') {
console.error('JSZip not loaded');
sendResponse({status: 'error', error: 'JSZip not loaded'});
return true;
}
// 创建并下载ZIP文件
createAndDownloadZip(message.files, message.siteName)
.then(() => {
console.log('ZIP creation completed');
})
.catch(error => {
console.error('ZIP creation failed:', error);
});
// 立即响应以避免连接丢失
sendResponse({status: 'processing'});
return true; // 保持通道开放
}
// 处理框架检测请求
if (message.type === 'detect_framework_in_page') {
const tabId = sender.tab?.id;
if (tabId) {
console.log('Executing framework detection in tab:', tabId);
// 使用executeScript在页面上下文中执行检测代码
chrome.scripting.executeScript({
target: { tabId: tabId },
function: detectFrameworkInPage
})
.then((results) => {
if (results && results[0] && results[0].result) {
const detectionResult = results[0].result;
console.log('Framework detection result:', detectionResult);
// 立即更新扩展图标
const framework = detectionResult.framework || 'unknown';
updateBadge(getFrameworkBadge(framework), tabId);
// 保存检测结果到storage以便popup打开时可以获取
chrome.storage.local.set({
[`framework_${tabId}`]: detectionResult
}, function() {
console.log('Framework detection result saved for tab:', tabId);
});
// 将结果发送回content script
chrome.tabs.sendMessage(tabId, {
type: 'framework_detection_result',
result: detectionResult
}, function(response) {
if (chrome.runtime.lastError) {
console.error('Error sending detection result to content script:', chrome.runtime.lastError);
} else {
console.log('Detection result sent to content script:', response);
}
});
}
})
.catch((error) => {
console.error('Error executing script:', error);
// 发送错误消息回content script
chrome.tabs.sendMessage(tabId, {
type: 'framework_detection_result',
result: { error: error.message || 'Failed to execute detection script' }
});
});
}
sendResponse({status: 'detecting'});
return true;
}
// 处理框架检测结果,更新扩展图标
if (message.type === 'framework_detected' && message.result) {
const tabId = sender.tab?.id;
if (tabId) {
const framework = message.result.framework || 'unknown';
updateBadge(getFrameworkBadge(framework), tabId);
}
sendResponse({status: 'badge_updated'});
return true;
}
});
// 在页面上下文中检测框架的函数
function detectFrameworkInPage() {
const result = {
framework: null,
router: null
};
try {
// 检测 Next.js
if (typeof window.__NEXT_DATA__ !== 'undefined') {
result.framework = 'nextjs';
// 尝试提取 Next.js 的路由信息
try {
if (window.__NEXT_DATA__.buildId && window.__NEXT_DATA__.page) {
result.router = {
buildId: window.__NEXT_DATA__.buildId,
currentPage: window.__NEXT_DATA__.page,
pages: window.__NEXT_DATA__.pages || []
};
}
} catch (e) {
console.error('Error extracting Next.js router info:', e);
}
return result;
}
// 检测 Next.js 方法2: 检查特定的 meta 和 script 标签
if (!!document.querySelector('meta[name="next-head"]')) {
result.framework = 'nextjs';
return result;
}
const nextScripts = Array.from(document.querySelectorAll('script[src^="/_next/"]'));
if (nextScripts.length > 0) {
result.framework = 'nextjs';
return result;
}
// 检测 Vue
// 方法1: 检查全局变量
if (typeof window.__VUE__ !== 'undefined' || typeof window.Vue !== 'undefined') {
result.framework = 'vue';
// 尝试获取Vue路由信息会在这里添加
// 简化处理,不提取路由以避免复杂性
return result;
}
// 方法2: 检查DOM元素上的 __vue__ 或 __vue_app__
const vueElements = document.querySelectorAll('*');
for (const el of vueElements) {
if (el.__vue__ || el.__vue_app__) {
result.framework = 'vue';
return result;
}
}
// 检测 React (如果没有检测到 Next.js)
// 方法1: 检查 React 相关的全局变量
if (typeof window.__REACT__ !== 'undefined' ||
typeof window.React !== 'undefined' ||
typeof window.__REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined') {
result.framework = 'react';
return result;
}
// 方法2: 检查 DOM 元素上的 React 属性
const rootElement = document.getElementById('root') || document.querySelector('*');
if (rootElement) {
for (const key in rootElement) {
if (key.startsWith('__react') || key.startsWith('__reactFiber$')) {
result.framework = 'react';
return result;
}
}
}
// 检测 Angular
if (window.angular || window.ng) {
result.framework = 'angular';
return result;
}
if (document && document.querySelector('[ng-version]')) {
result.framework = 'angular';
return result;
}
} catch (e) {
console.error('Framework detection error:', e);
}
return result;
}
// 获取框架对应的标识
function getFrameworkBadge(framework) {
switch (framework) {
case 'vue':
return 'Vue';
case 'react':
return 'R';
case 'nextjs':
return 'N.js';
case 'angular':
return 'Ang';
default:
return '?';
}
}
// 更新扩展图标上的徽章
function updateBadge(text, tabId = null) {
const action = {
text: { text }
};
// 设置徽章文本
if (tabId) {
chrome.action.setBadgeText({...action.text, tabId});
} else {
chrome.action.setBadgeText(action.text);
}
// 根据框架类型设置徽章颜色
let color = '#888888'; // 默认灰色
switch (text) {
case 'Vue':
color = '#42b883'; // Vue绿色
break;
case 'R':
color = '#61dafb'; // React蓝色
break;
case 'N.js':
color = '#000000'; // Next.js黑色
break;
case 'Ang':
color = '#dd0031'; // Angular红色
break;
}
// 设置徽章背景色
if (tabId) {
chrome.action.setBadgeBackgroundColor({color, tabId});
} else {
chrome.action.setBadgeBackgroundColor({color});
}
}
// 创建并下载ZIP文件
async function createAndDownloadZip(files, siteName) {
try {
console.log('Starting ZIP creation');
const zip = new JSZip();
// 添加文件到ZIP
let fileCount = 0;
for (const [filePath, content] of Object.entries(files)) {
// 去除开头斜杠
const normalizedPath = filePath.startsWith('/') ? filePath.substring(1) : filePath;
zip.file(normalizedPath, content);
fileCount++;
// 每100个文件记录一次进度
if (fileCount % 100 === 0) {
console.log(`Added ${fileCount} files to ZIP`);
}
}
console.log(`Added all ${fileCount} files to ZIP, generating blob`);
// 生成ZIP文件
const zipBlob = await zip.generateAsync({
type: 'blob',
compression: 'DEFLATE',
compressionOptions: { level: 6 } // 平衡速度和大小
});
console.log('ZIP blob generated, size:', (zipBlob.size / 1024 / 1024).toFixed(2), 'MB');
// 生成文件名
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `${siteName}-nextjs-source-${timestamp}.zip`;
// 下载ZIP文件 - 在Service Worker中不能使用URL.createObjectURL
console.log('Initiating download:', filename);
// 使用Data URL替代Object URL (仅适用于小型文件)
if (zipBlob.size < 10 * 1024 * 1024) { // 小于10MB的文件
// 读取blob为Data URL
const reader = new FileReader();
reader.onload = function() {
const dataUrl = reader.result;
console.log('Data URL created, length:', dataUrl.length);
// 使用Data URL下载
chrome.downloads.download({
url: dataUrl,
filename: filename,
saveAs: true
}, downloadId => {
if (chrome.runtime.lastError) {
console.error('Download failed:', chrome.runtime.lastError);
} else {
console.log('Download started with ID:', downloadId);
// 通知完成
chrome.runtime.sendMessage({
type: 'complete'
}, response => {
if (chrome.runtime.lastError) {
console.error('Failed to send complete message:', chrome.runtime.lastError);
}
});
}
});
};
reader.onerror = function(error) {
console.error('Error creating Data URL:', error);
throw new Error('Failed to create Data URL: ' + error);
};
// 开始读取Blob
reader.readAsDataURL(zipBlob);
} else {
// 对于大文件,使用分块下载或告知用户文件太大
console.warn('File too large for Data URL method (>10MB):', zipBlob.size);
// 通知用户文件过大
chrome.runtime.sendMessage({
type: 'error',
error: `ZIP file is too large (${(zipBlob.size / 1024 / 1024).toFixed(2)} MB). Try extracting fewer files.`
});
}
} catch (error) {
console.error('Error creating ZIP:', error);
// 发送错误消息
chrome.runtime.sendMessage({
type: 'error',
error: error.message || 'Unknown error creating ZIP'
}, response => {
if (chrome.runtime.lastError) {
console.error('Failed to send error message:', chrome.runtime.lastError);
}
});
throw error; // 重新抛出错误以便调用者处理
}
}