From a81a4dda6ff5f532665362edf58eb630b475e1be Mon Sep 17 00:00:00 2001 From: dragonwocky Date: Thu, 15 Dec 2022 01:04:56 +1100 Subject: [PATCH] refactor: platform-agnostic modloading --- scripts/enhance-desktop-app.mjs | 2 +- src/browser/api.js | 2 +- src/browser/init.js | 34 ++++----------------- src/browser/worker.js | 31 ++++++------------- src/common/loader.js | 34 +++++++++++++++++++++ src/common/registry.js | 25 +++++++--------- src/core/mod.json | 2 +- src/electron/{api.js => api.cjs} | 26 ++++++++-------- src/electron/client.js | 35 ---------------------- src/electron/{init.js => init.cjs} | 27 ++++++++++------- src/manifest.json | 45 +++++++++++++++++----------- src/mods/registry.json | 48 +----------------------------- 12 files changed, 120 insertions(+), 191 deletions(-) create mode 100644 src/common/loader.js rename src/electron/{api.js => api.cjs} (79%) delete mode 100644 src/electron/client.js rename src/electron/{init.js => init.cjs} (57%) diff --git a/scripts/enhance-desktop-app.mjs b/scripts/enhance-desktop-app.mjs index 44f599d..70f044c 100755 --- a/scripts/enhance-desktop-app.mjs +++ b/scripts/enhance-desktop-app.mjs @@ -145,7 +145,7 @@ const unpackApp = async () => { // create package.json // prettier-ignore const manifestPath = getResourcePath("app/node_modules/notion-enhancer/package.json"), - jsManifest = { ...manifest, main: "electron/init.js" }; + jsManifest = { ...manifest, main: "electron/init.cjs" }; // remove cli-specific fields delete jsManifest.bin; delete jsManifest.type; diff --git a/src/browser/api.js b/src/browser/api.js index aa073dd..e981503 100644 --- a/src/browser/api.js +++ b/src/browser/api.js @@ -44,7 +44,7 @@ const initDatabase = (namespace) => { }); if (!namespace) return obj; let entries = Object.entries(obj); - entries = entries.filter(([key]) => key.startsWith(`${namespace}__`)); + entries = entries.filter(([key]) => key.startsWith(namespace)); return Object.fromEntries(entries); }, populate: async (obj) => { diff --git a/src/browser/init.js b/src/browser/init.js index 344af76..7cbb1ab 100644 --- a/src/browser/init.js +++ b/src/browser/init.js @@ -1,37 +1,13 @@ -/* +/** * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (c) 2022 dragonwocky (https://dragonwocky.me/) * (https://notion-enhancer.github.io/) under the MIT license */ "use strict"; (async () => { - const enhancerApi = await import("./api.js"); - globalThis.__enhancerApi = enhancerApi; - // const site = location.host.endsWith('.notion.site'), - // page = location.pathname.split(/[/-]/g).reverse()[0].length === 32, - // whitelisted = ['/', '/onboarding'].includes(location.pathname), - // signedIn = localStorage['LRU:KeyValueStore2:current-user-id']; - - // if (site || page || (whitelisted && signedIn)) { - // const api = await import(chrome.runtime.getURL('api/index.mjs')), - // { fs, registry, web } = api; - - // for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { - // for (const sheet of mod.css?.client || []) { - // web.loadStylesheet(`repo/${mod._dir}/${sheet}`); - // } - // for (let script of mod.js?.client || []) { - // script = await import(fs.localPath(`repo/${mod._dir}/${script}`)); - // script.default(api, await registry.db(mod.id)); - // } - // } - - // const errors = await registry.errors(); - // if (errors.length) { - // console.log('[notion-enhancer] registry errors:'); - // console.table(errors); - // } - // } + await import("./api.js"); + await import("../common/registry.js"); + await import("../common/loader.js"); })(); diff --git a/src/browser/worker.js b/src/browser/worker.js index 48f5833..48e745c 100644 --- a/src/browser/worker.js +++ b/src/browser/worker.js @@ -4,40 +4,27 @@ * (https://notion-enhancer.github.io/) under the MIT license */ -'use strict'; +"use strict"; function focusMenu() { chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { - const url = chrome.runtime.getURL('repo/menu/menu.html'), + const url = chrome.runtime.getURL("repo/menu/menu.html"), menu = tabs.find((tab) => tab.url.startsWith(url)); if (menu) { - chrome.tabs.highlight({ 'tabs': menu.index }); + chrome.tabs.highlight({ tabs: menu.index }); } else chrome.tabs.create({ url }); }); } chrome.browserAction.onClicked.addListener(focusMenu); -function focusNotion() { - chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { - const notion = tabs.find((tab) => { - const url = new URL(tab.url), - matches = url.host.endsWith('.notion.so') || url.host.endsWith('.notion.site'); - return matches; - }); - if (notion) { - chrome.tabs.highlight({ 'tabs': notion.index }); - } else chrome.tabs.create({ url: 'https://notion.so/' }); - }); -} - function reload() { chrome.tabs.query({ windowId: chrome.windows.WINDOW_ID_CURRENT }, (tabs) => { - const menu = chrome.runtime.getURL('repo/menu/menu.html'); + const menu = chrome.runtime.getURL("repo/menu/menu.html"); tabs.forEach((tab) => { const url = new URL(tab.url), matches = - url.host.endsWith('.notion.so') || - url.host.endsWith('.notion.site') || + url.host.endsWith(".notion.so") || + url.host.endsWith(".notion.site") || tab.url.startsWith(menu); if (matches) chrome.tabs.reload(tab.id); }); @@ -46,13 +33,13 @@ function reload() { chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { switch (request.action) { - case 'focusMenu': + case "focusMenu": focusMenu(); break; - case 'focusNotion': + case "focusNotion": focusNotion(); break; - case 'reload': + case "reload": reload(); break; } diff --git a/src/common/loader.js b/src/common/loader.js new file mode 100644 index 0000000..5b6419c --- /dev/null +++ b/src/common/loader.js @@ -0,0 +1,34 @@ +/** + * notion-enhancer + * (c) 2021 dragonwocky (https://dragonwocky.me/) + * (https://notion-enhancer.github.io/) under the MIT license + */ + +"use strict"; + +(async () => { + const signedIn = localStorage["LRU:KeyValueStore2:current-user-id"], + pageLoaded = /(^\/$)|(-[0-9a-f]{32}$)/.test(location.pathname); + if (!signedIn || !pageLoaded) return; + + const { getMods, getProfile, isEnabled, enhancerUrl, initDatabase } = + globalThis.__enhancerApi; + for (const mod of await getMods()) { + if (!(await isEnabled(mod.id))) continue; + + // clientStyles + for (let stylesheet of mod.clientStyles ?? []) { + const $stylesheet = document.createElement("link"); + $stylesheet.rel = "stylesheet"; + $stylesheet.href = enhancerUrl(`${mod._src}/${stylesheet}`); + document.head.appendChild($stylesheet); + } + + // clientScripts + for (let script of mod.clientScripts ?? []) { + const db = initDatabase([await getProfile(), mod.id]); + script = await import(enhancerUrl(`${mod._src}/${script}`)); + script.default(globalThis.__enhancerApi, db); + } + } +})(); diff --git a/src/common/registry.js b/src/common/registry.js index 6ca5404..7c7f76f 100644 --- a/src/common/registry.js +++ b/src/common/registry.js @@ -7,24 +7,21 @@ "use strict"; let _core, _mods; -const getCore = () => { - _core ??= globalThis.__enhancerApi.readJson("/core/mod.json"); +const getCore = async () => { + _core ??= await globalThis.__enhancerApi.readJson("core/mod.json"); + _core._src = "core"; return _core; }, getMods = async () => { const { readJson } = globalThis.__enhancerApi; - _mods ??= await Promise.all([ + // prettier-ignore + _mods ??= (await Promise.all([ getCore(), - // prettier-ignore - ...(await readJson("/mods/registry.json")).map(async (modFolder) => { - try { - modFolder = `/mods/${modFolder}/mod.json`; - const modManifest = await readJson(modFolder); - modManifest._src = modFolder; - return modManifest; - } catch {} + ...(await readJson("mods/registry.json")).map(async (modFolder) => { + const modManifest = await readJson(`mods/${modFolder}/mod.json`); + return {...modManifest, _src: `mods/${modFolder}` }; }), - ]).filter((mod) => mod); + ])); return _mods; }, getThemes = async () => { @@ -51,8 +48,8 @@ const getProfile = async () => { mod = (await getMods()).find((mod) => mod.id === id); if (mod.platforms && !mod.platforms.includes(platform)) return false; const { initDatabase } = globalThis.__enhancerApi, - enabledMods = await initDatabase([await getProfile(), "enabledMods"]); - return Boolean(enabledMods.get(id)); + enabledMods = initDatabase([await getProfile(), "enabledMods"]); + return Boolean(await enabledMods.get(id)); }; globalThis.__enhancerApi ??= {}; diff --git a/src/core/mod.json b/src/core/mod.json index 13f4edd..16453de 100644 --- a/src/core/mod.json +++ b/src/core/mod.json @@ -40,5 +40,5 @@ ], "clientStyles": [], "clientScripts": [], - "electronScripts": {} + "electronScripts": [] } diff --git a/src/electron/api.js b/src/electron/api.cjs similarity index 79% rename from src/electron/api.js rename to src/electron/api.cjs index 7baab36..de3f8a2 100644 --- a/src/electron/api.js +++ b/src/electron/api.cjs @@ -14,7 +14,7 @@ const fs = require("fs"), const notionRequire = (target) => require(`../../../${target}`), notionPath = (target) => path.resolve(`${__dirname}/../../../${target}`); -const enhancerRequire = (target) => require(`../${target}`), +const enhancerRequire = (target) => require(`notion-enhancer/${target}`), enhancerPath = (target) => path.resolve(`${__dirname}/../${target}`), enhancerUrl = (target) => `notion://www.notion.so/__notion-enhancer/${target}`, @@ -52,22 +52,21 @@ const initDatabase = (namespace) => { db = __db ?? sqlite(enhancerConfig), init = db.prepare(`CREATE TABLE IF NOT EXISTS ${table} ( key TEXT PRIMARY KEY, - value TEXT, - mtime INTEGER + value TEXT )`); init.run(); __db = db; // prettier-ignore - const insert = db.prepare(`INSERT INTO ${table} (key, value, mtime) VALUES (?, ?, ?)`), + const insert = db.prepare(`INSERT INTO ${table} (key, value) VALUES (?, ?)`), // prettier-ignore - update = db.prepare(`UPDATE ${table} SET value = ?, mtime = ? WHERE key = ?`), + update = db.prepare(`UPDATE ${table} SET value = ? WHERE key = ?`), select = db.prepare(`SELECT * FROM ${table} WHERE key = ? LIMIT 1`), dump = db.prepare(`SELECT * FROM ${table}`), populate = db.transaction((obj) => { for (const key in obj) { - if (select.get(key)) update.run(value, key, Date.now()); - else insert.run(key, value, Date.now()); + if (select.get(key)) update.run(value, key); + else insert.run(key, value); } }); @@ -78,14 +77,15 @@ const initDatabase = (namespace) => { }, set: (key, value) => { key = key.startsWith(namespace) ? key : namespace + key; - if (select.get(key)) return update.run(value, key, Date.now()); - else return insert.run(key, value, Date.now()); + return select.get(key) === undefined + ? insert.run(key, value) + : update.run(value, key); }, dump: () => { - const rows = dump.all(); - let entries = rows.map(({ key, value }) => [key, value]); - if (!namespace) return Object.fromEntries(entries); - entries = entries.filter(([key]) => key.startsWith(`${namespace}__`)); + const entries = dump + .all() + .map(({ key, value }) => [key, value]) + .filter(([key]) => key.startsWith(namespace)); return Object.fromEntries(entries); }, populate, diff --git a/src/electron/client.js b/src/electron/client.js deleted file mode 100644 index 5c1df9a..0000000 --- a/src/electron/client.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * notion-enhancer - * (c) 2021 dragonwocky (https://dragonwocky.me/) - * (https://notion-enhancer.github.io/) under the MIT license - */ - -"use strict"; - -console.log(123); - -(async () => { - // const { getCore, getMods, enhancerPath } = globalThis.__enhancerApi; - // console.log(await getMods()); - // const page = location.pathname.split(/[/-]/g).reverse()[0].length === 32, - // whitelisted = ["/", "/onboarding"].includes(location.pathname), - // signedIn = localStorage["LRU:KeyValueStore2:current-user-id"]; - // if (page || (whitelisted && signedIn)) { - // const api = await import("./api/index.mjs"), - // { fs, registry, web } = api; - // for (const mod of await registry.list((mod) => registry.enabled(mod.id))) { - // for (const sheet of mod.css?.client || []) { - // web.loadStylesheet(`repo/${mod._dir}/${sheet}`); - // } - // for (let script of mod.js?.client || []) { - // script = await import(fs.localPath(`repo/${mod._dir}/${script}`)); - // script.default(api, await registry.db(mod.id)); - // } - // } - // const errors = await registry.errors(); - // if (errors.length) { - // console.error("[notion-enhancer] registry errors:"); - // console.table(errors); - // } - // } -})(); diff --git a/src/electron/init.js b/src/electron/init.cjs similarity index 57% rename from src/electron/init.js rename to src/electron/init.cjs index 93467f8..f3327f4 100644 --- a/src/electron/init.js +++ b/src/electron/init.cjs @@ -6,30 +6,37 @@ "use strict"; -require("./api"); +require("./api.cjs"); require("../common/registry.js"); module.exports = async (target, __exports, __eval) => { + const { + getMods, + getProfile, + isEnabled, + enhancerRequire, + enhancerUrl, + initDatabase, + } = globalThis.__enhancerApi; + // clientScripts if (target === "renderer/preload") { - const { enhancerUrl } = globalThis.__enhancerApi; document.addEventListener("readystatechange", (event) => { if (document.readyState !== "complete") return false; - const script = document.createElement("script"); - script.type = "module"; - script.src = enhancerUrl("electron/client.js"); - document.head.appendChild(script); + const $script = document.createElement("script"); + $script.type = "module"; + $script.src = enhancerUrl("common/loader.js"); + document.head.appendChild($script); }); } // electronScripts - const { getMods, getProfile, initDatabase } = globalThis.__enhancerApi; for (const mod of await getMods()) { - if (!mod.electronScripts || !isEnabled(mod.id)) continue; + if (!mod.electronScripts || !(await isEnabled(mod.id))) continue; for (const { source, target: targetScript } of mod.electronScripts) { if (`${target}.js` !== targetScript) continue; - const script = require(`notion-enhancer/repo/${mod._dir}/${source}`), - db = await initDatabase([await getProfile(), mod.id]); + const script = enhancerRequire(`${mod._src}/${source}`), + db = initDatabase([await getProfile(), mod.id]); script(globalThis.__enhancerApi, db, __exports, __eval); } } diff --git a/src/manifest.json b/src/manifest.json index d25d120..2b569be 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,30 +1,39 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "notion-enhancer", - "version": "0.11.0", + "version": "0.11.1", "author": "dragonwocky (https://dragonwocky.me/)", "description": "an enhancer/customiser for the all-in-one productivity workspace notion.so", "homepage_url": "https://notion-enhancer.github.io", "icons": { - "16": "media/colour-x16.png", - "32": "media/colour-x32.png", - "48": "media/colour-x48.png", - "128": "media/colour-x128.png", - "256": "media/colour-x256.png", - "512": "media/colour-x512.png" + "16": "/media/colour-x16.png", + "32": "/media/colour-x32.png", + "48": "/media/colour-x48.png", + "128": "/media/colour-x128.png", + "256": "/media/colour-x256.png", + "512": "/media/colour-x512.png" }, - "browser_action": {}, - "background": { "scripts": ["worker.js"] }, - "options_ui": { - "page": "mods/menu/menu.html", - "open_in_tab": true - }, - "web_accessible_resources": ["browser/*", "common/*", "vendor/*", "media/*", "mods/*"], + "action": {}, + "background": { "service_worker": "/browser/worker.js" }, + "options_page": "/core/menu.html", "content_scripts": [ { - "matches": ["https://*.notion.so/*", "https://*.notion.site/*"], - "js": ["browser/init.js"] + "matches": ["*://*.notion.so/*", "https://*.notion.site/*"], + "js": ["/browser/init.js"] } ], - "permissions": ["tabs", "storage", "clipboardRead", "clipboardWrite", "unlimitedStorage"] + "web_accessible_resources": [ + { + "matches": ["*://*.notion.so/*", "https://*.notion.site/*"], + "resources": ["/*"] + } + ], + "permissions": [ + "tabs", + "storage", + "clipboardRead", + "clipboardWrite", + "unlimitedStorage" + ], + "host_permissions": ["*://*.notion.so/*", "*://*.notion.site/*"] } diff --git a/src/mods/registry.json b/src/mods/registry.json index 6093766..fe51488 100644 --- a/src/mods/registry.json +++ b/src/mods/registry.json @@ -1,47 +1 @@ -[ - "menu", - "theming", - "components", - - "tweaks", - "font-chooser", - - "integrated-titlebar", - "tray", - "tabs", - "always-on-top", - "view-scale", - - "outliner", - "scroll-to-top", - "indentation-lines", - "right-to-left", - "simpler-databases", - "emoji-sets", - "bypass-preview", - "topbar-icons", - "word-counter", - "code-line-numbers", - "calendar-scroll", - "collapsible-properties", - "weekly-view", - "truncated-titles", - "focus-mode", - "global-block-links", - - "icon-sets", - "quick-note", - - "material-ocean", - "cherry-cola", - "dark+", - "light+", - "dracula", - "pastel-dark", - "neutral", - "nord", - "gruvbox-dark", - "gruvbox-light", - "playful-purple", - "pinky-boom" -] +[]