diff --git a/src/app/service/content/gm_api.ts b/src/app/service/content/gm_api.ts index 708c83496..b59acb5b8 100644 --- a/src/app/service/content/gm_api.ts +++ b/src/app/service/content/gm_api.ts @@ -633,36 +633,32 @@ export default class GMApi extends GM_Base { if (details.data instanceof FormData) { // 处理FormData param.dataType = "FormData"; - const data: Array = []; const keys: { [key: string]: boolean } = {}; details.data.forEach((val, key) => { keys[key] = true; }); // 处理FormData中的数据 - await Promise.all( - Object.keys(keys).map((key) => { - const values = (details.data).getAll(key); - return Promise.all( - values.map(async (val) => { - if (val instanceof File) { - const url = await a.CAT_createBlobUrl(val); - data.push({ - key, - type: "file", - val: url, - filename: val.name, - }); - } else { - data.push({ + const data = (await Promise.all( + Object.keys(keys).flatMap((key) => + (details.data).getAll(key).map((val) => + val instanceof File + ? a.CAT_createBlobUrl(val).then( + (url) => + ({ + key, + type: "file", + val: url, + filename: val.name, + }) as GMSend.XHRFormData + ) + : ({ key, type: "text", val, - }); - } - }) - ); - }) - ); + } as GMSend.XHRFormData) + ) + ) + )) as GMSend.XHRFormData[]; param.data = data; } else if (details.data instanceof Blob) { // 处理blob diff --git a/src/app/service/offscreen/gm_api.ts b/src/app/service/offscreen/gm_api.ts index 1a460695f..3aa9f60e0 100644 --- a/src/app/service/offscreen/gm_api.ts +++ b/src/app/service/offscreen/gm_api.ts @@ -148,10 +148,14 @@ export default class GMApi { const data = new FormData(); if (details.data && details.data instanceof Array) { await Promise.all( - details.data.map(async (val: GMSend.XHRFormData) => { + details.data.map((val: GMSend.XHRFormData) => { if (val.type === "file") { - const file = new File([await (await fetch(val.val)).blob()], val.filename!); - data.append(val.key, file, val.filename); + return fetch(val.val) + .then((res) => res.blob()) + .then((blob) => { + const file = new File([blob], val.filename!); + data.append(val.key, file, val.filename); + }); } else { data.append(val.key, val.val); } diff --git a/src/app/service/service_worker/fetch.ts b/src/app/service/service_worker/fetch.ts index 68c4f73a9..e81cb3af2 100644 --- a/src/app/service/service_worker/fetch.ts +++ b/src/app/service/service_worker/fetch.ts @@ -80,16 +80,17 @@ export async function fetchIconByDomain(domain: string): Promise { - try { - const res = await fetch((fetchingUrl = icon), { method: "HEAD", signal: timeoutAbortSignal(timeout) }); - if (res.ok && checkFileNameEqual(res.url, icon)) { - return res.url; - } - } catch { - // 忽略错误 - } - }) + icons.map((icon) => + fetch((fetchingUrl = icon), { method: "HEAD", signal: timeoutAbortSignal(timeout) }) + .then((res) => { + if (res.ok && checkFileNameEqual(res.url, icon)) { + return res.url; + } + }) + .catch(() => { + // 忽略错误 + }) + ) ); return msgResponse(0, urls.filter((url) => !!url) as string[]); diff --git a/src/app/service/service_worker/runtime.ts b/src/app/service/service_worker/runtime.ts index 29055728c..3b67efd76 100644 --- a/src/app/service/service_worker/runtime.ts +++ b/src/app/service/service_worker/runtime.ts @@ -472,36 +472,36 @@ export class RuntimeService { enableScript.push(scriptRes as ScriptLoadInfo); } - await Promise.all([ - // 加载value - ...enableScript.map(async (script) => { - const value = await this.value.getScriptValue(script!); - script.value = value; - }), - // 加载resource - ...enableScript.map(async (script) => { - const resource = await this.resource.getScriptResources(script, false); - script.resource = resource; - Object.keys(script.resource).forEach((name) => { - const res = script.resource[name]; - // 删除base64以节省资源 - // 如果有content就删除base64 - if (res.content) { - res.base64 = undefined; + const { value, resource, scriptDAO } = this; + await Promise.all( + enableScript.flatMap((script) => [ + // 加载value + value.getScriptValue(script!).then((value) => { + script.value = value; + }), + // 加载resource + resource.getScriptResources(script, false).then((resource) => { + for (const name of Object.keys(resource)) { + const res = script.resource[name]; + // 删除base64以节省资源 + // 如果有content就删除base64 + if (res.content) { + res.base64 = undefined; + } } - }); - }), - // 加载code相关的信息 - ...enableScript.map(async (script) => { - const code = await this.scriptDAO.scriptCodeDAO.get(script.uuid); - if (code) { - const metadataStr = getMetadataStr(code.code) || ""; - const userConfigStr = getUserConfigStr(code.code) || ""; - script.metadataStr = metadataStr; - script.userConfigStr = userConfigStr; - } - }), - ]); + script.resource = resource; + }), + // 加载code相关的信息 + scriptDAO.scriptCodeDAO.get(script.uuid).then((code) => { + if (code) { + const metadataStr = getMetadataStr(code.code) || ""; + const userConfigStr = getUserConfigStr(code.code) || ""; + script.metadataStr = metadataStr; + script.userConfigStr = userConfigStr; + } + }), + ]) + ); // 更新资源使用了file协议的脚本 const needUpdateRegisteredUserScripts = enableScript.filter((script) => { diff --git a/src/app/service/service_worker/script.ts b/src/app/service/service_worker/script.ts index d8a211838..dbaad6e0e 100644 --- a/src/app/service/service_worker/script.ts +++ b/src/app/service/service_worker/script.ts @@ -326,9 +326,8 @@ export class ScriptService { return this.scriptCodeDAO.get(uuid); } - async buildScriptRunResource(script: Script): Promise { + buildScriptRunResource(script: Script): Promise { const ret: ScriptRunResource = Object.assign(script); - // 自定义配置 if (ret.selfMetadata) { ret.metadata = { ...ret.metadata }; @@ -336,20 +335,21 @@ export class ScriptService { ret.metadata[key] = ret.selfMetadata![key]; }); } - - ret.value = await this.valueService.getScriptValue(ret); - - ret.resource = await this.resourceService.getScriptResources(ret, true); - - ret.flag = randomMessageFlag(); - const code = await this.getCode(ret.uuid); - if (!code) { - throw new Error("code is null"); - } - ret.code = code.code; - ret.code = compileScriptCode(ret); - - return ret; + return Promise.all([ + this.valueService.getScriptValue(ret), + this.resourceService.getScriptResources(ret, true), + this.getCode(script.uuid), + ]).then(([value, resource, code]) => { + if (!code) { + throw new Error("code is null"); + } + ret.value = value; + ret.resource = resource; + ret.flag = randomMessageFlag(); + ret.code = code.code; + ret.code = compileScriptCode(ret); + return ret; + }); } async excludeUrl({ uuid, url, remove }: { uuid: string; url: string; remove: boolean }) { diff --git a/src/app/service/service_worker/synchronize.ts b/src/app/service/service_worker/synchronize.ts index ef4e7c14b..7f024820d 100644 --- a/src/app/service/service_worker/synchronize.ts +++ b/src/app/service/service_worker/synchronize.ts @@ -103,7 +103,7 @@ export class SynchronizeService { } // 获取所有脚本 const list = await this.scriptDAO.all(); - return Promise.all(list.map(async (script): Promise => this.generateScriptBackupData(script))); + return Promise.all(list.map((script) => this.generateScriptBackupData(script))); } async generateScriptBackupData(script: Script): Promise { @@ -178,20 +178,14 @@ export class SynchronizeService { requiresCss: ResourceBackup[]; }) { const { uuid, requires, resources, requiresCss } = data; - const ret: Promise[] = []; - // 处理requires - requires.forEach((item) => { - ret.push(this.resource.importResource(uuid, item, "require")); - }); - // 处理resources - resources.forEach((item) => { - ret.push(this.resource.importResource(uuid, item, "resource")); - }); - // 处理requiresCss - requiresCss.forEach((item) => { - ret.push(this.resource.importResource(uuid, item, "require-css")); - }); - return Promise.all(ret).then(() => { + return Promise.all([ + // 处理requires + ...requires.map((item) => this.resource.importResource(uuid, item, "require")), + // 处理resources + ...resources.map((item) => this.resource.importResource(uuid, item, "resource")), + // 处理requiresCss + ...requiresCss.map((item) => this.resource.importResource(uuid, item, "require-css")), + ]).then(() => { return; }); } diff --git a/src/app/service/service_worker/utils.ts b/src/app/service/service_worker/utils.ts index 2837d8436..2c0e307db 100644 --- a/src/app/service/service_worker/utils.ts +++ b/src/app/service/service_worker/utils.ts @@ -1,12 +1,3 @@ -export function isExtensionRequest( - details: chrome.webRequest.OnBeforeRequestDetails & { originUrl?: string } -): boolean { - return !!( - (details.initiator && chrome.runtime.getURL("").startsWith(details.initiator)) || - (details.originUrl && details.originUrl.startsWith(chrome.runtime.getURL(""))) - ); -} - export function getRunAt(runAts: string[]): chrome.extensionTypes.RunAt { if (runAts.length === 0) { return "document_idle"; @@ -20,48 +11,6 @@ export function getRunAt(runAts: string[]): chrome.extensionTypes.RunAt { return "document_idle"; } -export function mapToObject(map: Map): { [key: string]: any } { - const obj: { [key: string]: any } = {}; - map.forEach((value, key) => { - if (value instanceof Map) { - obj[key] = mapToObject(value); - } else if (obj[key] instanceof Array) { - obj[key].push(value); - } else { - obj[key] = value; - } - }); - return obj; -} - -export function objectToMap(obj: { [key: string]: any }): Map { - const map = new Map(); - Object.keys(obj).forEach((key) => { - if (obj[key] instanceof Map) { - map.set(key, objectToMap(obj[key])); - } else if (obj[key] instanceof Array) { - map.set(key, obj[key]); - } else { - map.set(key, obj[key]); - } - }); - return map; -} - -export function arrayToObject(arr: Array): any[] { - const obj: any[] = []; - arr.forEach((item) => { - if (item instanceof Map) { - obj.push(mapToObject(item)); - } else if (item instanceof Array) { - obj.push(arrayToObject(item)); - } else { - obj.push(item); - } - }); - return obj; -} - // 检查是不是base64编码 export function isBase64(str: string): boolean { if (typeof str !== "string" || str.length === 0) { diff --git a/src/locales/locales.ts b/src/locales/locales.ts index 1fd4fd003..761a6447d 100644 --- a/src/locales/locales.ts +++ b/src/locales/locales.ts @@ -81,32 +81,33 @@ export function isChineseUser() { } // 匹配语言 -export async function matchLanguage() { - const acceptLanguages = await chrome.i18n.getAcceptLanguages(); - // 遍历数组寻找匹配语言 - for (let i = 0; i < acceptLanguages.length; i += 1) { - const lng = acceptLanguages[i]; - if (i18n.hasResourceBundle(lng, "translation")) { - return lng; +export function matchLanguage(): Promise { + return chrome.i18n.getAcceptLanguages().then((acceptLanguages) => { + // 遍历数组寻找匹配语言 + for (let i = 0; i < acceptLanguages.length; i += 1) { + const lng = acceptLanguages[i]; + if (i18n.hasResourceBundle(lng, "translation")) { + return lng; + } } - } - // 根据前缀去匹配 - const prefixMap = {} as Partial>; - for (const lng of i18n.languages) { - const prefix = lng.split("-")[0]; - if (!prefixMap[prefix]) { - prefixMap[prefix] = []; + // 根据前缀去匹配 + const prefixMap = {} as Partial>; + for (const lng of i18n.languages) { + const prefix = lng.split("-")[0]; + if (!prefixMap[prefix]) { + prefixMap[prefix] = []; + } + prefixMap[prefix].push(lng); } - prefixMap[prefix].push(lng); - } - for (let i = 0; i < acceptLanguages.length; i += 1) { - const lng = acceptLanguages[i]; - const prefix = lng.split("-")[0]; - if (prefixMap[prefix] && prefixMap[prefix].length > 0) { - return prefixMap[prefix][0]; // 返回第一个匹配的语言 + for (let i = 0; i < acceptLanguages.length; i += 1) { + const lng = acceptLanguages[i]; + const prefix = lng.split("-")[0]; + if (prefixMap[prefix] && prefixMap[prefix].length > 0) { + return prefixMap[prefix][0]; // 返回第一个匹配的语言 + } } - } - return ""; + return ""; + }); } export { t }; diff --git a/src/pages/components/CodeEditor/index.tsx b/src/pages/components/CodeEditor/index.tsx index 9b42b6bbd..f258dbe85 100644 --- a/src/pages/components/CodeEditor/index.tsx +++ b/src/pages/components/CodeEditor/index.tsx @@ -25,13 +25,13 @@ const CodeEditor: React.ForwardRefRenderFunction<{ editor: editor.IStandaloneCod })); useEffect(() => { - const loadConfigs = async () => { - const [eslintConfig, enableEslint] = await Promise.all([ - systemConfig.getEslintConfig(), - systemConfig.getEnableEslint(), - ]); - setEslintConfig(eslintConfig); - setEnableEslint(enableEslint); + const loadConfigs = () => { + Promise.all([systemConfig.getEslintConfig(), systemConfig.getEnableEslint()]).then( + ([eslintConfig, enableEslint]) => { + setEslintConfig(eslintConfig); + setEnableEslint(enableEslint); + } + ); }; loadConfigs(); }, []); diff --git a/src/pages/options/routes/Setting.tsx b/src/pages/options/routes/Setting.tsx index 0dfc90b42..73e57b898 100644 --- a/src/pages/options/routes/Setting.tsx +++ b/src/pages/options/routes/Setting.tsx @@ -51,22 +51,8 @@ function Setting() { }); useEffect(() => { - const loadConfigs = async () => { - const [ - cloudSync, - menuExpandNum, - checkCycle, - updateDisabled, - silenceUpdate, - eslintConfig, - enableEslint, - language, - blacklist, - badgeNumberType, - badgeBackgroundColor, - badgeTextColor, - scriptMenuDisplayType, - ] = await Promise.all([ + const loadConfigs = () => { + Promise.all([ systemConfig.getCloudSync(), systemConfig.getMenuExpandNum(), systemConfig.getCheckScriptUpdateCycle(), @@ -80,25 +66,41 @@ function Setting() { systemConfig.getBadgeBackgroundColor(), systemConfig.getBadgeTextColor(), systemConfig.getScriptMenuDisplayType(), - ]); - - setSyncDelete(cloudSync.syncDelete); - setSyncScriptStatus(cloudSync.syncStatus); - setEnableCloudSync(cloudSync.enable); - setFilesystemType(cloudSync.filesystem); - setFilesystemParam(cloudSync.params[cloudSync.filesystem] || {}); - setMenuExpandNum(menuExpandNum); - setCheckScriptUpdateCycle(checkCycle); - setUpdateDisableScript(updateDisabled); - setSilenceUpdateScript(silenceUpdate); - setEslintConfig(eslintConfig); - setEnableEslint(enableEslint); - setLanguage(language); - setBlacklist(blacklist); - setBadgeNumberType(badgeNumberType); - setBadgeBackgroundColor(badgeBackgroundColor); - setBadgeTextColor(badgeTextColor); - setScriptMenuDisplayType(scriptMenuDisplayType); + ]).then( + ([ + cloudSync, + menuExpandNum, + checkCycle, + updateDisabled, + silenceUpdate, + eslintConfig, + enableEslint, + language, + blacklist, + badgeNumberType, + badgeBackgroundColor, + badgeTextColor, + scriptMenuDisplayType, + ]) => { + setSyncDelete(cloudSync.syncDelete); + setSyncScriptStatus(cloudSync.syncStatus); + setEnableCloudSync(cloudSync.enable); + setFilesystemType(cloudSync.filesystem); + setFilesystemParam(cloudSync.params[cloudSync.filesystem] || {}); + setMenuExpandNum(menuExpandNum); + setCheckScriptUpdateCycle(checkCycle); + setUpdateDisableScript(updateDisabled); + setSilenceUpdateScript(silenceUpdate); + setEslintConfig(eslintConfig); + setEnableEslint(enableEslint); + setLanguage(language); + setBlacklist(blacklist); + setBadgeNumberType(badgeNumberType); + setBadgeBackgroundColor(badgeBackgroundColor); + setBadgeTextColor(badgeTextColor); + setScriptMenuDisplayType(scriptMenuDisplayType); + } + ); }; loadConfigs(); diff --git a/src/pkg/backup/export.ts b/src/pkg/backup/export.ts index 5c34d002b..57a30b439 100644 --- a/src/pkg/backup/export.ts +++ b/src/pkg/backup/export.ts @@ -12,70 +12,82 @@ export default class BackupExport { } // 导出备份数据 - async export(data: BackupData): Promise { + export(data: BackupData): Promise { // 写入脚本备份 - const results: Promise[] = []; - data.script.forEach((item) => { - results.push(this.writeScript(item)); + return Promise.all([ + ...data.script.flatMap((item) => this.writeScript(item)), + ...data.subscribe.flatMap((item) => this.writeSubscribe(item)), + ]).then(() => { + return; }); - data.subscribe.forEach((item) => { - results.push(this.writeSubscribe(item)); - }); - await Promise.all(results); } - async writeScript(script: ScriptBackupData) { + writeScript(script: ScriptBackupData): Promise[] { const { name } = script.options!.meta; // 将脚本名中的特殊字符替换为下划线 const filename = name.replace(/[\\/\\:*?"<>|]/g, "_"); + // 写脚本文件 - await (await this.fs.create(`${filename}.user.js`)).write(script.code); + const writeCode = script.code; + // 写入脚本options.json - await (await this.fs.create(`${filename}.options.json`)).write(JSON.stringify(script.options)); + const writeOptions = JSON.stringify(script.options); + // 写入脚本storage.json // 不想兼容tm的导出规则了,直接写入storage.json const storage = { ...script.storage }; - Object.keys(storage.data).forEach((key: string) => { - storage.data[key] = toStorageValueStr(storage.data[key]); - }); - await (await this.fs.create(`${filename}.storage.json`)).write(JSON.stringify(storage)); - // 写入脚本资源文件 - await this.writeResource(filename, script.resources, "resources"); - await this.writeResource(filename, script.requires, "requires"); - await this.writeResource(filename, script.requiresCss, "requires.css"); + const { data } = storage; + for (const key of Object.keys(data)) { + data[key] = toStorageValueStr(data[key]); + } + const wrtieStorage = JSON.stringify(storage); - return; + return [ + // 写脚本文件 + this.fs.create(`${filename}.user.js`).then((fileWriter) => fileWriter.write(writeCode)), + // 写入脚本options.json + this.fs.create(`${filename}.options.json`).then((fileWriter) => fileWriter.write(writeOptions)), + // 写入脚本storage.json + this.fs.create(`${filename}.storage.json`).then((fileWriter) => fileWriter.write(wrtieStorage)), + // 写入脚本资源文件 + ...this.writeResource(filename, script.resources, "resources"), + ...this.writeResource(filename, script.requires, "requires"), + ...this.writeResource(filename, script.requiresCss, "requires.css"), + ]; } - async writeResource( + writeResource( filename: string, resources: ResourceBackup[], type: "resources" | "requires" | "requires.css" - ): Promise { - const results: Promise[] = resources.map(async (item) => { + ): Promise[] { + return resources.flatMap((item) => { // md5是tm的导出规则 const md5 = md5OfText(`${type}{val.meta.url}`); - if (item.source) { - await (await this.fs.create(`${filename}.user.js-${md5}-${item.meta.name}`)).write(item.source!); - } else { - await (await this.fs.create(`${filename}.user.js-${md5}-${item.meta.name}`)).write(base64ToBlob(item.base64)); - } - (await this.fs.create(`${filename}.user.js-${md5}-${item.meta.name}.${type}.json`)).write( - JSON.stringify(item.meta) - ); + const writeSource = item.source || base64ToBlob(item.base64); + const writeMeta = JSON.stringify(item.meta); + return [ + this.fs + .create(`${filename}.user.js-${md5}-${item.meta.name}`) + .then((fileWriter) => fileWriter.write(writeSource)), + this.fs + .create(`${filename}.user.js-${md5}-${item.meta.name}.${type}.json`) + .then((fileWriter) => fileWriter.write(writeMeta)), + ]; }); - return Promise.all(results); } - async writeSubscribe(subscribe: SubscribeBackupData) { + writeSubscribe(subscribe: SubscribeBackupData): Promise[] { const { name } = subscribe.options!.meta; // 将订阅名中的特殊字符替换为下划线 const filename = name.replace(/[\\/\\:*?"<>|]/g, "_"); - // 写入订阅文件 - await (await this.fs.create(`${filename}.user.sub.js`)).write(subscribe.source); - // 写入订阅options.json - await (await this.fs.create(`${filename}.user.sub.options.json`)).write(JSON.stringify(subscribe.options)); - - return; + const writeSource = subscribe.source; + const writeOptions = JSON.stringify(subscribe.options); + return [ + // 写入订阅文件 + this.fs.create(`${filename}.user.sub.js`).then((fileWriter) => fileWriter.write(writeSource)), + // 写入订阅options.json + this.fs.create(`${filename}.user.sub.options.json`).then((fileWriter) => fileWriter.write(writeOptions)), + ]; } } diff --git a/src/pkg/config/config.ts b/src/pkg/config/config.ts index b42554353..f7ee367ec 100644 --- a/src/pkg/config/config.ts +++ b/src/pkg/config/config.ts @@ -22,6 +22,8 @@ export type CATFileStorage = { status: "unset" | "success" | "error"; }; +type WithAsyncValue = T | { asyncValue?: () => Promise }; + export class SystemConfig { private readonly cache = new Map(); @@ -41,15 +43,20 @@ export class SystemConfig { }); } - get(key: string, defaultValue: Exclude): Promise { + get( + key: string, + defaultValue: WithAsyncValue> + ): Promise { if (this.cache.has(key)) { let val = this.cache.get(key); - val = (val === undefined ? defaultValue : val) as T; + //@ts-ignore + val = (val === undefined ? defaultValue?.asyncValue?.() || defaultValue : val) as T | Promise; return Promise.resolve(val); } return this.storage.get(key).then((val) => { this.cache.set(key, val); - val = (val === undefined ? defaultValue : val) as T; + //@ts-ignore + val = (val === undefined ? defaultValue?.asyncValue?.() || defaultValue : val) as T | Promise; return val; }); } @@ -225,12 +232,20 @@ export class SystemConfig { return cachedLanguage; } } - const lng = await this.get("language", (await matchLanguage()) || chrome.i18n.getUILanguage()); - // 设置进入缓存 - if (globalThis.localStorage) { - localStorage.setItem("language", lng); - } - return lng; + return this.get("language", { + // 取预设值时呼叫 asyncValue 进行异步取值 + asyncValue() { + return matchLanguage().then((matchLanguageRes) => { + return matchLanguageRes || chrome.i18n.getUILanguage(); + }); + }, + }).then((lng) => { + // 设置进入缓存 + if (globalThis.localStorage) { + localStorage.setItem("language", `${lng}`); + } + return lng; + }); } setLanguage(value: string) {