Skip to content

Commit 3cd5ebb

Browse files
committed
升级到ManifestV3
1 parent 5a5871f commit 3cd5ebb

9 files changed

+1313
-1230
lines changed

package-lock.json

+1,150-1,070
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "markdown-downloader",
3-
"version": "1.0.8",
3+
"version": "2.0.0",
44
"description": "markdown文章下载",
55
"main": "dist/index.js",
66
"scripts": {
@@ -45,6 +45,7 @@
4545
"mathjax": "^3.2.2",
4646
"md5": "^2.3.0",
4747
"path-browserify": "^1.0.1",
48+
"qs": "^6.13.0",
4849
"webpack": "^5.63.0",
4950
"webpack-cli": "^4.9.1",
5051
"webpack-merge": "^5.8.0"

src/background.js

+24-55
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,30 @@
1+
import { ajax, blob2array } from './request'
12
import { configs } from './websites'
2-
import downloadZip from './download'
3-
4-
const getHeaders = (xhr, responseHeaders) => {
5-
const headers = {}
6-
if (Array.isArray(responseHeaders)) {
7-
responseHeaders.map(item => {
8-
headers[item] = xhr.getResponseHeader(item)
9-
})
10-
}
11-
return headers
12-
}
13-
14-
const sendCallback = (sendResponse, responseHeaders) => {
15-
return {
16-
sendSuccess (message, xhr) {
17-
sendResponse([null, message, getHeaders(xhr, responseHeaders), xhr])
18-
},
19-
sendError (error, xhr) {
20-
sendResponse([error, null, getHeaders(xhr, responseHeaders), xhr])
21-
}
22-
}
23-
}
243

254
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
26-
const { type, responseHeaders } = message
27-
const { url, data, dataType, callback } = message
28-
const { fileName, files, options = {} } = message
29-
const { sendSuccess, sendError } = sendCallback(sendResponse, responseHeaders)
30-
if (/(get|post)/i.test(type)) {
31-
ajax({
32-
url,
33-
method: type,
34-
data,
35-
dataType,
36-
success (data, xhr) {
37-
if (/text|blob/i.test(dataType)) {
38-
sendSuccess(data, xhr)
39-
} else {
40-
const obj = dataType === 'text' ? data : JSON.parse(data)
41-
const result = /^json$/i.test(dataType) ? {
42-
callback: noop(callback),
43-
data: obj
44-
} : data
45-
if (typeof callback === 'string' && callback) {
46-
sendSuccess(result, xhr)
47-
} else {
48-
sendSuccess(obj, xhr)
49-
}
50-
}
51-
},
52-
error (error, xhr) {
53-
sendError(error, xhr)
5+
const { type } = message
6+
const { url, data, dataType } = message
7+
if (/(get|post)/i.test(type)) {
8+
ajax({
9+
url,
10+
method: type,
11+
data,
12+
dataType,
13+
success: async (data) => {
14+
if (/blob/i.test(dataType)) {
15+
sendResponse([null, {
16+
data: await blob2array(data),
17+
mimeType: data.type
18+
}])
19+
} else {
20+
sendResponse([null, data])
5421
}
55-
})
56-
} else if (type === 'download') {
57-
downloadZip(fileName, files, options)
58-
}
22+
},
23+
error (error) {
24+
sendResponse([error, null])
25+
}
26+
})
27+
}
5928
return true
6029
})
6130

@@ -71,7 +40,7 @@ const sendMessage = (message, onsuccess) => {
7140
})
7241
}
7342

74-
chrome.browserAction.onClicked.addListener((tab) => {
43+
chrome.action.onClicked.addListener((tab) => {
7544
const { host } = new URL(tab.url)
7645
const matched = configs.some(({ website, hosts }) => {
7746
if (

src/download.js

+2-83
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,8 @@
11
import md5 from 'md5'
22
import JSZip from 'jszip'
33
import FileSaver from 'jszip/vendor/FileSaver'
4-
import { getLocalOptions } from './utils'
5-
6-
const defaultOptions = {
7-
partLimit: 1e3,
8-
requestLimit: 5,
9-
retry: 3
10-
}
11-
12-
const options = Object.assign({}, defaultOptions)
13-
14-
export const mergeOptions = (newOptions) => {
15-
return Object.assign(options, defaultOptions, newOptions instanceof Object ? newOptions : {})
16-
}
17-
export const mergeLocalOptions = async () => {
18-
return Object.assign(mergeOptions(await getLocalOptions()))
19-
}
20-
21-
export const noop = (func, defaultFunc) => {
22-
return typeof func === 'function' ? func : typeof defaultFunc === 'function' ? defaultFunc : () => {}
23-
}
24-
25-
export const ajax = async (options) => {
26-
const config = await mergeLocalOptions()
27-
const core = (retry = 3) => {
28-
const xhr = new XMLHttpRequest()
29-
const errorCB = (err) => {
30-
if (retry--) {
31-
setTimeout(() => {
32-
core(retry - 1)
33-
}, 3e3)
34-
} else {
35-
console.log(err)
36-
noop(options.error)(err, xhr)
37-
}
38-
}
39-
options.method = options.method || 'get'
40-
xhr.responseType = options.dataType || 'json'
41-
xhr.onreadystatechange = () => {
42-
if (xhr.readyState == 4) {
43-
if (xhr.status === 200) {
44-
try {
45-
noop(options.success)(xhr.response, xhr)
46-
} catch (err) {
47-
errorCB(err)
48-
}
49-
} else {
50-
errorCB(new Error('network error'))
51-
}
52-
}
53-
}
54-
xhr.error = errorCB
55-
if (/post/i.test(options.method)) {
56-
xhr.open(options.method, options.url, options.async !== false)
57-
xhr.setRequestHeader('Content-type', /json/i.test(options.dataType) ? 'application/json' : 'application/x-www-form-urlencoded')
58-
xhr.send(options.data)
59-
} else {
60-
xhr.open(options.method, options.url, options.async !== false)
61-
xhr.send()
62-
}
63-
}
64-
core(config.retry)
65-
}
66-
67-
export const fetchBlobFile = (file) =>{
68-
return new Promise((resolve, reject) => {
69-
if (file.content) {
70-
resolve(new Blob([file.content], {type : file.type || 'text/plain'}))
71-
} else {
72-
ajax({
73-
url: file.downloadUrl,
74-
type: 'get',
75-
data: '',
76-
dataType: 'blob',
77-
success: (blob) => {
78-
resolve(blob)
79-
},
80-
error: () => {
81-
resolve(new Blob([file.downloadUrl], {type : 'text/plain'}))
82-
}
83-
})
84-
}
85-
})
86-
}
4+
import { options, mergeLocalOptions } from './options'
5+
import { fetchBlobFile } from './request'
876

887
export const partTask = (items, handler, limit) => {
898
let index = 0

src/index.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
import { websites } from './websites'
22
import {
33
isExtension,
4-
sendMessage,
5-
getLocalOptions
4+
sendMessage
65
} from './utils'
6+
import downloadZip from './download'
77
import { downloadMarkdown } from './markdown'
8+
import { getLocalOptions } from './options'
89

910
const extract = async (options, customOptions, hook) => {
1011
const localOptions = await getLocalOptions()
1112
const data = await downloadMarkdown(options, Object.assign(customOptions, {
1213
localOptions
1314
}), hook)
14-
data && sendMessage(data)
15+
if (data) {
16+
if (data.type === 'download') {
17+
const { fileName, files, options = {} } = data
18+
downloadZip(fileName, files, options)
19+
} else {
20+
sendMessage(data)
21+
}
22+
}
1523
return data
1624
}
1725

src/manifest.json

+22-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"manifest_version": 2,
2+
"manifest_version": 3,
33
"name": "markdown文档下载",
44
"version": "1.0",
55
"description": "",
@@ -9,11 +9,9 @@
99
"128": "icon.png"
1010
},
1111
"background": {
12-
"scripts": [
13-
"background.js"
14-
]
12+
"service_worker": "background.js"
1513
},
16-
"browser_action": {
14+
"action": {
1715
"default_icon": "icon.png"
1816
},
1917
"options_page": "option.html",
@@ -32,13 +30,28 @@
3230
"contextMenus",
3331
"tabs",
3432
"notifications",
35-
"webRequest",
36-
"webRequestBlocking",
3733
"storage",
34+
"webRequest",
35+
"webNavigation",
36+
"scripting"
37+
],
38+
"host_permissions": [
3839
"http://*/*",
3940
"https://*/*"
4041
],
42+
"externally_connectable": {
43+
"matches": [
44+
"http://*/*",
45+
"https://*/*"
46+
]
47+
},
4148
"web_accessible_resources": [
42-
"inject.js"
43-
]
49+
{
50+
"resources": ["inject.js"],
51+
"matches": ["http://*/*", "https://*/*"]
52+
}
53+
],
54+
"content_security_policy": {
55+
"extension_pages": "script-src 'self'; object-src 'self'"
56+
}
4457
}

src/options.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const defaultOptions = {
2+
partLimit: 1e3,
3+
requestLimit: 5,
4+
retry: 3
5+
}
6+
7+
export const options = Object.assign({}, defaultOptions)
8+
9+
export const getLocalOptions = () => {
10+
return new Promise((resolve) => {
11+
chrome.storage.local.get('localOptions', ({ localOptions }) => {
12+
resolve(localOptions instanceof Object ? localOptions : {})
13+
})
14+
})
15+
}
16+
export const mergeOptions = (newOptions) => {
17+
return Object.assign(options, defaultOptions, newOptions instanceof Object ? newOptions : {})
18+
}
19+
export const mergeLocalOptions = async () => {
20+
return Object.assign(mergeOptions(await getLocalOptions()))
21+
}
22+
23+
export default {
24+
options,
25+
getLocalOptions,
26+
mergeOptions,
27+
mergeLocalOptions
28+
}

src/request.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import qs from 'qs'
2+
import { mergeLocalOptions } from './options'
3+
const noop = (func, defaultFunc) => {
4+
return typeof func === 'function' ? func : typeof defaultFunc === 'function' ? defaultFunc : () => {}
5+
}
6+
export const blob2array = (blob) => {
7+
return new Promise((resolve, reject) => {
8+
const reader = new FileReader()
9+
reader.onload = () => {
10+
resolve(Array.from(new Uint8Array(reader.result)))
11+
}
12+
reader.onerror = reject
13+
reader.readAsArrayBuffer(blob)
14+
})
15+
}
16+
export const array2blob = (array, mimeType = '') => {
17+
return new Blob([new Uint8Array(array)], { type: mimeType });
18+
}
19+
20+
export const fetchBlobFile = (file) => {
21+
return new Promise((resolve) => {
22+
if (file.content) {
23+
resolve(new Blob([file.content], {type : file.type || 'text/plain'}))
24+
} else {
25+
chrome.runtime.sendMessage({ type: 'get', url: file.downloadUrl, dataType: 'blob' }, ([error, message]) => {
26+
if (chrome.runtime.lastError) {
27+
console.error(chrome.runtime.lastError.message);
28+
return;
29+
}
30+
if (error) {
31+
resolve(new Blob([file.downloadUrl], {type : 'text/plain'}))
32+
} else {
33+
resolve(array2blob(message.data, message.mimeType || 'text/plain'))
34+
}
35+
})
36+
}
37+
})
38+
}
39+
40+
export const ajax = async (options) => {
41+
const config = await mergeLocalOptions()
42+
const core = (retry = 3) => {
43+
const errorCB = (err) => {
44+
if (retry-- > 0) {
45+
setTimeout(() => {
46+
core(retry - 1)
47+
}, 3e3)
48+
} else {
49+
noop(options.error)(err)
50+
}
51+
}
52+
const { method = 'get', data, dataType, headers, success } = options
53+
const fetchOptions = {
54+
method,
55+
headers: headers instanceof Object ? headers : {}
56+
}
57+
if (/get/i.test(method)) {
58+
options.url += (/\?/.test(options.url) ? '&' : '?') + qs.stringify(data)
59+
} else {
60+
fetchOptions.body = JSON.stringify(data)
61+
}
62+
fetch(options.url, Object.assign(fetchOptions)).then(res => {
63+
return res[dataType] ? res[dataType]() : res.text()
64+
}).then(success).catch(errorCB)
65+
}
66+
core(config.retry)
67+
}
68+
69+
export default {
70+
ajax,
71+
fetchBlobFile
72+
}

0 commit comments

Comments
 (0)