mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-03 12:19:02 +00:00
180 lines
6.5 KiB
JavaScript
Executable File
180 lines
6.5 KiB
JavaScript
Executable File
/**
|
|
* notion-enhancer
|
|
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
|
|
* (https://notion-enhancer.github.io/) under the MIT license
|
|
*/
|
|
|
|
import asar from "@electron/asar";
|
|
import os from "node:os";
|
|
import fsp from "node:fs/promises";
|
|
import { existsSync } from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import { join, resolve } from "node:path";
|
|
import { execSync } from "node:child_process";
|
|
import { createRequire } from "node:module";
|
|
|
|
import patch from "./patch-desktop-app.mjs";
|
|
|
|
let __notionResources;
|
|
const nodeRequire = createRequire(import.meta.url),
|
|
manifest = nodeRequire("../package.json"),
|
|
platform =
|
|
process.platform === "linux" &&
|
|
os.release().toLowerCase().includes("microsoft")
|
|
? "wsl"
|
|
: process.platform,
|
|
polyfillWslEnv = (name) => {
|
|
if (platform !== "wsl" || process.env[name]) return process.env[name];
|
|
// adds a windows environment variable to process.env
|
|
// in a wsl environment, inc. path conversion
|
|
const value = execSync(`cmd.exe /c echo %${name}%`, {
|
|
encoding: "utf8",
|
|
stdio: "pipe",
|
|
}).trim(),
|
|
isAbsolutePath = /^[a-zA-Z]:[\\\/]/.test(value),
|
|
onSystemDrive = /^[\\\/]/.test(value);
|
|
if (isAbsolutePath) {
|
|
// e.g. C:\Program Files
|
|
const drive = value[0].toLowerCase(),
|
|
path = value.slice(2).replace(/\\/g, "/");
|
|
process.env[name] = `/mnt/${drive}${path}`;
|
|
} else if (onSystemDrive) {
|
|
// e.g. \Program Files
|
|
const drive = polyfillWslEnv("SYSTEMDRIVE")[0].toLowerCase(),
|
|
path = value.replace(/\\/g, "/");
|
|
process.env[name] = `/mnt/${drive}${path}`;
|
|
} else process.env[name] = value;
|
|
return process.env[name];
|
|
},
|
|
readdirDeep = async (dir) => {
|
|
dir = resolve(dir);
|
|
let files = [];
|
|
for (let file of await fsp.readdir(dir)) {
|
|
if (["node_modules", ".git"].includes(file)) continue;
|
|
file = join(dir, file);
|
|
const stat = await fsp.lstat(file);
|
|
if (stat.isDirectory()) {
|
|
files = files.concat(await readdirDeep(file));
|
|
} else if (stat.isSymbolicLink()) {
|
|
} else files.push(file);
|
|
}
|
|
return files;
|
|
};
|
|
|
|
const setNotionPath = (path) => {
|
|
// sets notion resource path to user provided value
|
|
// e.g. with the --path cli option
|
|
__notionResources = path;
|
|
},
|
|
getResourcePath = (path) => {
|
|
if (__notionResources) return resolve(`${__notionResources}/${path}`);
|
|
polyfillWslEnv("LOCALAPPDATA");
|
|
polyfillWslEnv("PROGRAMW6432");
|
|
const potentialPaths = {
|
|
win32: [
|
|
resolve(`${process.env.LOCALAPPDATA}/Programs/Notion/resources`),
|
|
resolve(`${process.env.PROGRAMW6432}/Notion/resources`),
|
|
],
|
|
darwin: [
|
|
`/Users/${process.env.USER}/Applications/Notion.app/Contents/Resources`,
|
|
"/Applications/Notion.app/Contents/Resources",
|
|
],
|
|
linux: ["/opt/notion-app"],
|
|
};
|
|
potentialPaths["wsl"] = potentialPaths["win32"];
|
|
for (const testPath of potentialPaths[platform]) {
|
|
if (!existsSync(testPath)) continue;
|
|
__notionResources = testPath;
|
|
return resolve(`${__notionResources}/${path}`);
|
|
}
|
|
},
|
|
// prefer unpacked if both exist
|
|
getAppPath = () => ["app", "app.asar"].map(getResourcePath).find(existsSync),
|
|
getBackupPath = () =>
|
|
["app.bak", "app.asar.bak"].map(getResourcePath).find(existsSync),
|
|
checkEnhancementVersion = () => {
|
|
// prettier-ignore
|
|
const manifestPath = getResourcePath("app/node_modules/notion-enhancer/package.json");
|
|
if (!existsSync(manifestPath)) return undefined;
|
|
const insertVersion = nodeRequire(manifestPath).version;
|
|
return insertVersion;
|
|
};
|
|
|
|
const unpackApp = async () => {
|
|
const appPath = getAppPath();
|
|
if (!appPath || !appPath.endsWith("asar")) return false;
|
|
// asar reads synchronously
|
|
asar.extractAll(appPath, appPath.replace(/\.asar$/, ""));
|
|
await fsp.rm(appPath);
|
|
return true;
|
|
},
|
|
applyEnhancements = async () => {
|
|
const appPath = getAppPath();
|
|
if (!appPath || appPath.endsWith("asar")) return false;
|
|
const srcPath = fileURLToPath(new URL("../src", import.meta.url)),
|
|
insertPath = getResourcePath("app/node_modules/notion-enhancer");
|
|
if (existsSync(insertPath)) await fsp.rm(insertPath, { recursive: true });
|
|
// insert the notion-enhancer/src folder into notion's node_modules folder
|
|
await fsp.cp(srcPath, insertPath, { recursive: true });
|
|
// call patch-desktop-app.mjs on each file
|
|
// prettier-ignore
|
|
const notionScripts = (await readdirDeep(appPath))
|
|
.filter((file) => file.endsWith(".js")),
|
|
scriptUpdates = [];
|
|
for (const file of notionScripts) {
|
|
const scriptId = file.slice(appPath.length + 1, -3).replace(/\\/g, "/"),
|
|
scriptContent = await fsp.readFile(file, { encoding: "utf8" }),
|
|
patchedContent = await patch(scriptId, scriptContent),
|
|
changesMade = patchedContent !== scriptContent;
|
|
if (changesMade) scriptUpdates.push(fsp.writeFile(file, patchedContent));
|
|
}
|
|
// create package.json
|
|
// prettier-ignore
|
|
const manifestPath = getResourcePath("app/node_modules/notion-enhancer/package.json"),
|
|
jsManifest = { ...manifest, main: "init.js" };
|
|
// remove cli-specific fields
|
|
delete jsManifest.bin;
|
|
delete jsManifest.type;
|
|
delete jsManifest.scripts;
|
|
delete jsManifest.engines;
|
|
delete jsManifest.packageManager;
|
|
delete jsManifest.dependencies;
|
|
const jsonManifest = JSON.stringify(jsManifest);
|
|
scriptUpdates.push(fsp.writeFile(manifestPath, jsonManifest));
|
|
await Promise.all(scriptUpdates);
|
|
return true;
|
|
},
|
|
takeBackup = async () => {
|
|
const appPath = getAppPath();
|
|
if (!appPath) return false;
|
|
const backupPath = getBackupPath();
|
|
if (backupPath) await fsp.rm(backupPath, { recursive: true });
|
|
const destPath = `${appPath}.bak`;
|
|
if (!appPath.endsWith(".asar")) {
|
|
await fsp.cp(appPath, destPath, { recursive: true });
|
|
} else await fsp.rename(appPath, destPath);
|
|
return true;
|
|
},
|
|
restoreBackup = async () => {
|
|
const backupPath = getBackupPath();
|
|
if (!backupPath) return false;
|
|
const destPath = backupPath.replace(/\.bak$/, "");
|
|
if (existsSync(destPath)) await fsp.rm(destPath, { recursive: true });
|
|
await fsp.rename(backupPath, destPath);
|
|
const appPath = getAppPath();
|
|
if (destPath !== appPath) await fsp.rm(appPath, { recursive: true });
|
|
return true;
|
|
};
|
|
|
|
export {
|
|
getResourcePath,
|
|
getAppPath,
|
|
getBackupPath,
|
|
checkEnhancementVersion,
|
|
setNotionPath,
|
|
unpackApp,
|
|
applyEnhancements,
|
|
takeBackup,
|
|
restoreBackup,
|
|
};
|