feat(cli): safer patch process to avoid repeat-patching

This commit is contained in:
dragonwocky 2022-12-06 23:23:43 +11:00
parent ebf15dbfb9
commit 979616e032
Signed by: dragonwocky
GPG Key ID: 7998D08F7D7BD7A8
17 changed files with 210 additions and 445 deletions

137
bin.mjs
View File

@ -67,15 +67,15 @@ const hideCursor = () => process.stdout.write("\x1b[?25l"),
}); });
}); });
}, },
promptForValue = async (prompt, values) => { promptInput = async (prompt) => {
let input; let input;
// prevent line clear remove existing stdout // prevent line clear remove existing stdout
print`\n`; print`\n`;
do { do {
// clear line and continue prompting until valid input is received // clear line and continue prompting until valid input is received
print`\x1b[1A\r\x1b[K${prompt}`; print`\x1b[1A\r\x1b[K {inverse > ${prompt} [Y/n]:} `;
input = (await readStdin()).trim(); input = (await readStdin()).trim();
} while (!values.includes(input)); } while (!["Y", "y", "N", "n"].includes(input));
return input; return input;
}; };
@ -89,13 +89,13 @@ const commands = [
// ["alias, option=example", [type, "description"]] // ["alias, option=example", [type, "description"]]
[ [
"--path=</path/to/notion/resources>", "--path=</path/to/notion/resources>",
[String, "provide notion installation location (defaults to auto-detected)"], [String, "manually provide a notion installation location"],
], ],
["--backup", [Boolean, ""]], ["--backup", [Boolean, ""]],
["--overwrite", [Boolean, ""]], ["--overwrite", [Boolean, "overwrite inserted enhancements (for rapid development)"]],
["-y, --yes", [Boolean, 'skip prompts; assume "yes" and run non-interactively']], ["-y, --yes", [Boolean, 'skip prompts; assume "yes" and run non-interactively']],
["-n, --no", [Boolean, 'skip prompts; assume "no" and run non-interactively']], ["-n, --no", [Boolean, 'skip prompts; assume "no" and run non-interactively']],
["-q, --quiet", [Boolean, 'skip prompts and hide all output; assume "no"']], ["-q, --quiet", [Boolean, 'skip prompts; assume "no" and hide all output']],
["-d, --debug", [Boolean, "show detailed error messages"]], ["-d, --debug", [Boolean, "show detailed error messages"]],
["-j, --json", [Boolean, "display json output (where applicable)"]], ["-j, --json", [Boolean, "display json output (where applicable)"]],
["-h, --help", [Boolean, "display usage information"]], ["-h, --help", [Boolean, "display usage information"]],
@ -174,7 +174,6 @@ const args = arg(compileOptsToArgSpec(options)),
print`${enhancerVersion} via ${nodeVersion} on ${osVersion}\n`; print`${enhancerVersion} via ${nodeVersion} on ${osVersion}\n`;
} }
}; };
if (args["--quiet"]) __quiet = true; if (args["--quiet"]) __quiet = true;
if (args["--help"]) [printHelp(), process.exit()]; if (args["--help"]) [printHelp(), process.exit()];
if (args["--version"]) [printVersion(), process.exit()]; if (args["--version"]) [printVersion(), process.exit()];
@ -185,34 +184,20 @@ const defaultPromptValue = args["--yes"]
? "n" ? "n"
: undefined; : undefined;
const notionNotFound = `notion installation not found (corrupted or nonexistent)`, const appPath = getAppPath(),
enhancerNotApplied = `notion-enhancer not applied (notion installation found)`, backupPath = getBackupPath(),
cachePath = getCachePath(),
insertVersion = checkEnhancementVersion(),
onVersionMismatch = `notion-enhancer v${insertVersion} applied != v${manifest.version} current`,
onNotionNotFound = `notion installation not found (corrupted or nonexistent)`,
onEnhancerNotApplied = `notion-enhancer not applied (notion installation found)`,
onSuccess = chalk`{bold.whiteBright SUCCESS} {green ✔}`, onSuccess = chalk`{bold.whiteBright SUCCESS} {green ✔}`,
onFail = chalk`{bold.whiteBright FAILURE} {red ✘}`, onFail = chalk`{bold.whiteBright FAILURE} {red ✘}`,
onCancel = chalk`{bold.whiteBright CANCELLED} {red ✘}`; onCancel = chalk`{bold.whiteBright CANCELLED} {red ✘}`;
switch (args["_"][0]) { const removeEnhancementsVerbose = async () => {
case "apply": { if (appPath) {
print`{bold.whiteBright [NOTION-ENHANCER] APPLY} `; print` {grey * ${onNotionNotFound}: skipping}\n`;
// const res = await apply(notionPath, {
// overwritePrevious: promptRes,
// patchPrevious: opts.get("patch") ? true : false,
// takeBackup: opts.get("no-backup") ? false : true,
// });
// if (res) {
// log`{bold.whiteBright SUCCESS} {green ✔}`;
// } else log`{bold.whiteBright CANCELLED} {red ✘}`;
break;
}
case "remove": {
const appPath = getAppPath(),
backupPath = getBackupPath(),
cachePath = getCachePath(),
insertVersion = checkEnhancementVersion();
print`{bold.whiteBright [NOTION-ENHANCER] REMOVE}\n`;
// restore notion to app.bak or app.asar.bak
if (!appPath) {
print` {grey * ${notionNotFound}}\n`;
} else if (insertVersion) { } else if (insertVersion) {
print` {grey * notion installation found: notion-enhancer v${insertVersion} applied}\n`; print` {grey * notion installation found: notion-enhancer v${insertVersion} applied}\n`;
if (backupPath) { if (backupPath) {
@ -225,17 +210,17 @@ switch (args["_"][0]) {
print` {red * to remove the notion-enhancer from notion, uninstall notion and then install}\n`; print` {red * to remove the notion-enhancer from notion, uninstall notion and then install}\n`;
print` {red a vanilla version of the app from https://www.notion.so/desktop (mac, windows)}\n`; print` {red a vanilla version of the app from https://www.notion.so/desktop (mac, windows)}\n`;
print` {red or ${manifest.homepage}/getting-started/installation (linux)\n}`; print` {red or ${manifest.homepage}/getting-started/installation (linux)\n}`;
return false;
} }
} else print` {grey * ${enhancerNotApplied}: skipping}\n`; } else print` {grey * ${onEnhancerNotApplied}: skipping}\n`;
return true;
},
removeCacheVerbose = async () => {
// optionally remove ~/.notion-enhancer // optionally remove ~/.notion-enhancer
if (existsSync(cachePath)) { if (!existsSync(cachePath)) {
print` {grey * cache found: ${cachePath}}\n`; print` {grey * cache found: ${cachePath}}\n`;
let deleteCache; const deleteCache = defaultPromptValue ?? (await promptInput("delete?"));
const prompt = chalk` {inverse > delete? [Y/n]:} `; if (defaultPromptValue) print` {inverse > delete? [Y/n]:} ${deleteCache}\n`;
if (defaultPromptValue) {
deleteCache = defaultPromptValue;
print`${prompt}${defaultPromptValue}\n`;
} else deleteCache = await promptForValue(prompt, ["Y", "y", "N", "n"]);
if (["Y", "y"].includes(deleteCache)) { if (["Y", "y"].includes(deleteCache)) {
print` {grey * cache found: removing}`; print` {grey * cache found: removing}`;
startSpinner(); startSpinner();
@ -243,15 +228,65 @@ switch (args["_"][0]) {
stopSpinner(); stopSpinner();
} else print` {grey * cache found: keeping}\n`; } else print` {grey * cache found: keeping}\n`;
} else print` {grey * cache not found: skipping}\n`; } else print` {grey * cache not found: skipping}\n`;
// failure if backup could not be restored return true;
print`${insertVersion && !backupPath ? onFail : onSuccess}\n`; };
switch (args["_"][0]) {
case "apply": {
print`{bold.whiteBright [NOTION-ENHANCER] APPLY} `;
// notion not installed
if (!appPath) throw Error(onNotionNotFound);
// same version already applied
if (insertVersion === manifest.version && !args["--overwrite"]) {
print` {grey * notion-enhancer v${insertVersion} already applied}`;
} else {
// diff version already applied
if (insertVersion && insertVersion !== manifest.version) {
print` {grey * ${onVersionMismatch}}`;
const deleteCache = defaultPromptValue ?? (await promptInput("update?"));
if (defaultPromptValue) print` {inverse > update? [Y/n]:} ${deleteCache}\n`;
if (["Y", "y"].includes(deleteCache)) {
print` {grey * different version found: removing}`;
startSpinner();
await removeCacheVerbose();
stopSpinner();
} else print` {grey * different version found: keeping}\n`;
}
}
// let s;
// if (status.executable.endsWith(".asar")) {
// s = spinner(" * unpacking app files").loop();
// ...
// s.stop();
// }
// if (status.code === 0 && takeBackup) {
// s = spinner(" * backing up default app").loop();
// ...
// s.stop();
// }
// const res = await apply(notionPath, {
// overwritePrevious: promptRes,
// patchPrevious: opts.get("patch") ? true : false,
// takeBackup: opts.get("no-backup") ? false : true,
// });
// if (res) {
// log`{bold.whiteBright SUCCESS} {green ✔}`;
// } else log`{bold.whiteBright CANCELLED} {red ✘}`;
break; break;
} }
case "check":
const appPath = getAppPath(), case "remove": {
backupPath = getBackupPath(), print`{bold.whiteBright [NOTION-ENHANCER] REMOVE}\n`;
cachePath = getCachePath(), let success = await removeEnhancementsVerbose();
insertVersion = checkEnhancementVersion(); success = (await removeCacheVerbose()) && success;
// failure if backup could not be restored
print`${success ? onSuccess : onFail}\n`;
break;
}
case "check": {
if (args["--json"]) { if (args["--json"]) {
printObject({ printObject({
appPath, appPath,
@ -264,13 +299,15 @@ switch (args["_"][0]) {
} }
print`{bold.whiteBright [NOTION-ENHANCER] CHECK:} `; print`{bold.whiteBright [NOTION-ENHANCER] CHECK:} `;
if (manifest.version === insertVersion) { if (manifest.version === insertVersion) {
print`notion-enhancer v${manifest.version} applied\n`; print`notion-enhancer v${insertVersion} applied\n`;
} else if (insertVersion) { } else if (insertVersion) {
print`notion-enhancer v${manifest.version} applied != v${insertVersion} cli\n`; print`${onVersionMismatch}\n`;
} else if (appPath) { } else if (appPath) {
print`${enhancerNotApplied}.\n`; print`${onEnhancerNotApplied}\n`;
} else print`${notionNotFound}.\n`; } else print`${onNotionNotFound}\n`;
break; break;
}
default: default:
printHelp(); printHelp();
} }

View File

@ -1,142 +0,0 @@
/**
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
import chalk from 'chalk';
export const log = (strs, ...tags) => {
if (!Array.isArray(strs)) strs = [strs];
if (!strs.raw) strs.raw = [...strs];
console.log(chalk(strs, ...tags));
};
export const cursor = {
hide: () => process.stdout.write('\x1b[?25l'),
show: () => process.stdout.write('\x1b[?25h'),
};
export const line = {
clear: () => process.stdout.write('\r\x1b[K'),
backspace: (n = 1) => process.stdout.write('\b'.repeat(n)),
write: (string) => process.stdout.write(string),
prev: (n = 1) => process.stdout.write(`\x1b[${n}A`),
next: (n = 1) => process.stdout.write(`\x1b[${n}B`),
forward: (n = 1) => process.stdout.write(`\x1b[${n}C`),
back: (n = 1) => process.stdout.write(`\x1b[${n}D`),
new: () => process.stdout.write('\n'),
async read(prompt = '', values = []) {
let input = '';
prompt = [prompt];
prompt.raw = [prompt[0]];
prompt = chalk(prompt);
this.new();
do {
this.prev();
this.clear();
this.write(prompt);
input = await new Promise((res, rej) => {
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.once('data', (key) => {
process.stdin.pause();
res(key.slice(0, -1));
});
});
} while (values.length && !values.includes(input));
return input;
},
};
export let lastSpinner;
export const spinner = (
message,
frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
complete = '→'
) => {
if (lastSpinner?.interval) lastSpinner.stop();
const spinner = {
interval: undefined,
i: 0,
step() {
this.i = (this.i + 1) % frames.length;
line.backspace(3);
line.write(chalk` {bold.yellow ${frames[this.i]}} `);
cursor.hide();
return this;
},
loop(ms = 80) {
if (this.interval) clearInterval(this.interval);
this.interval = setInterval(() => this.step(), ms);
return this;
},
stop() {
if (this.interval) {
clearInterval(this.interval);
this.interval = undefined;
}
line.backspace(3);
line.write(chalk` {bold.yellow ${complete}}\n`);
cursor.show();
return this;
},
};
line.write(chalk`${message} {bold.yellow ${frames[spinner.i]}} `);
lastSpinner = spinner;
return spinner;
};
export const args = () => process.argv.slice(2).filter((arg) => !arg.startsWith('-'));
export const options = (aliases = {}) => {
return new Map(
process.argv
.slice(2)
.filter((arg) => arg.startsWith('-'))
.map((arg) => {
let opt,
val = true;
if (arg.startsWith('--')) {
if (arg.includes('=')) {
[opt, val] = arg.slice(2).split(/=((.+)|$)/);
} else opt = arg.slice(2);
} else {
opt = arg.slice(1);
}
if (parseInt(val).toString() === val) val = +val;
if (aliases[opt]) opt = aliases[opt];
return [opt, val];
})
);
};
export const help = ({
name = process.argv[1].split('/').reverse()[0],
usage = `${name} <command> [options]`,
version = '',
link = '',
commands = [],
options = [],
}) => {
if (version) version = ' v' + version;
const cmdPad = Math.max(...commands.map((cmd) => cmd[0].length)),
optPad = Math.max(...options.map((opt) => opt[0].length));
commands = commands
.map((cmd) => ` ${cmd[0].padEnd(cmdPad)} ${chalk`{grey :}`} ${cmd[1]}`)
.join('\n');
options = options
.map((opt) => ` ${opt[0].padEnd(optPad)} ${chalk`{grey :}`} ${opt[1]}`)
.join('\n');
log`{bold.rgb(245,245,245) ${name}${version}}`;
if (link) log`{grey ${link}}`;
log`\n{bold.rgb(245,245,245) USAGE}`;
log`{yellow $} ${usage}`;
log`\n{bold.rgb(245,245,245) COMMANDS}`;
log`${commands}`;
log`\n{bold.rgb(245,245,245) OPTIONS}`;
log`${options}`;
};

View File

@ -7,12 +7,16 @@
import asar from "@electron/asar"; import asar from "@electron/asar";
import os from "node:os"; import os from "node:os";
import { promises as fsp, existsSync } from "node:fs"; import { promises as fsp, existsSync } from "node:fs";
import { resolve } from "node:path"; import { fileURLToPath } from "node:url";
import { join, resolve } from "node:path";
import { execSync } from "node:child_process"; import { execSync } from "node:child_process";
import { createRequire } from "node:module"; import { createRequire } from "node:module";
import patch from "./patch-desktop-app.mjs";
let __notionResources, __enhancerCache; let __notionResources, __enhancerCache;
const nodeRequire = createRequire(import.meta.url), const nodeRequire = createRequire(import.meta.url),
manifest = nodeRequire("../package.json"),
platform = platform =
process.platform === "linux" && os.release().toLowerCase().includes("microsoft") process.platform === "linux" && os.release().toLowerCase().includes("microsoft")
? "wsl" ? "wsl"
@ -39,6 +43,21 @@ const nodeRequire = createRequire(import.meta.url),
process.env[name] = `/mnt/${drive}${path}`; process.env[name] = `/mnt/${drive}${path}`;
} else process.env[name] = value; } else process.env[name] = value;
return process.env[name]; 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) => { const setNotionPath = (path) => {
@ -78,8 +97,8 @@ const setNotionPath = (path) => {
checkEnhancementVersion = () => { checkEnhancementVersion = () => {
const insertPath = getResourcePath("app/node_modules/notion-enhancer"); const insertPath = getResourcePath("app/node_modules/notion-enhancer");
if (!existsSync(insertPath)) return undefined; if (!existsSync(insertPath)) return undefined;
const insertManifest = getResourcePath("app/node_modules/notion-enhancer/package.json"), const manifestPath = getResourcePath("app/node_modules/notion-enhancer/package.json"),
insertVersion = nodeRequire(insertManifest).version; insertVersion = nodeRequire(manifestPath).version;
return insertVersion; return insertVersion;
}; };
@ -89,10 +108,42 @@ const unpackApp = () => {
asar.extractAll(appPath, appPath.replace(/\.asar$/, "")); asar.extractAll(appPath, appPath.replace(/\.asar$/, ""));
return true; return true;
}, },
applyEnhancements = () => { applyEnhancements = async () => {
const appPath = getAppPath(); const appPath = getAppPath();
if (!appPath || appPath.endsWith("asar")) return false; 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,
filter: (_, dest) => {
// exclude browser-specific files
const browserDest = getResourcePath("app/node_modules/notion-enhancer/browser");
return dest !== browserDest;
},
});
// patch-desktop-app.mjs
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
const manifestPath = getResourcePath("app/node_modules/notion-enhancer/package.json"),
insertManifest = { ...manifest, main: "desktop/init.cjs" };
// remove cli-specific fields
delete insertManifest.bin;
delete insertManifest.type;
delete insertManifest.engines;
delete insertManifest.dependencies;
delete insertManifest.packageManager;
scriptUpdates.push(fsp.writeFile(manifestPath, JSON.stringify(insertManifest)));
await Promise.all(scriptUpdates);
return true; return true;
}, },
takeBackup = async () => { takeBackup = async () => {

View File

@ -1,110 +0,0 @@
/**
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import os from 'os';
import fs from 'fs';
import fsp from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import { execSync } from 'child_process';
export const __dirname = (meta) => path.dirname(fileURLToPath(meta.url));
export const pkg = (filepath = `${__dirname(import.meta)}/../package.json`) => {
try {
return JSON.parse(fs.readFileSync(path.resolve(filepath)));
} catch {
return {};
}
};
export const platform =
process.platform === 'linux' && os.release().toLowerCase().includes('microsoft')
? 'wsl'
: process.platform;
let __notion;
export const findNotion = () => {
if (__notion) return __notion;
switch (platform) {
case 'darwin':
__notion = '';
const userInstall = `/Users/${process.env.USER}/Applications/Notion.app/Contents/Resources`,
globalInstall = '/Applications/Notion.app/Contents/Resources';
if (fs.existsSync(userInstall)) {
__notion = userInstall;
} else if (fs.existsSync(globalInstall)) {
__notion = globalInstall;
}
break;
case 'win32':
__notion = process.env.LOCALAPPDATA + '\\Programs\\Notion\\resources';
break;
case 'wsl':
const [drive, ...windowsPath] = execSync('cmd.exe /c echo %localappdata%', {
encoding: 'utf8',
stdio: 'pipe',
});
__notion = `/mnt/${drive.toLowerCase()}${windowsPath
.slice(1, -2)
.join('')
.replace(/\\/g, '/')}/Programs/Notion/resources`;
break;
case 'linux':
// https://aur.archlinux.org/packages/notion-app/
if (fs.existsSync('/opt/notion-app')) __notion = '/opt/notion-app';
}
return __notion;
};
let __enhancerCache;
export const findEnhancerCache = () => {
if (__enhancerCache) return __enhancerCache;
let home = os.homedir();
if (platform === 'wsl') {
const [drive, ...windowsPath] = execSync('cmd.exe /c echo %systemdrive%%homepath%', {
encoding: 'utf8',
stdio: 'pipe',
});
home = `/mnt/${drive.toLowerCase()}${windowsPath
.slice(1, -2)
.join('')
.replace(/\\/g, '/')}`;
}
__enhancerCache = path.resolve(`${home}/.notion-enhancer`);
return __enhancerCache;
};
export const copyDir = async (src, dest) => {
src = path.resolve(src);
dest = path.resolve(dest);
if (!fs.existsSync(dest)) await fsp.mkdir(dest);
for (let file of await fsp.readdir(src)) {
const stat = await fsp.lstat(path.join(src, file));
if (stat.isDirectory()) {
await copyDir(path.join(src, file), path.join(dest, file));
} else if (stat.isSymbolicLink()) {
await fsp.symlink(await fsp.readlink(path.join(src, file)), path.join(dest, file));
} else await fsp.copyFile(path.join(src, file), path.join(dest, file));
}
return true;
};
export const readDirDeep = async (dir) => {
dir = path.resolve(dir);
let files = [];
for (let file of await fsp.readdir(dir)) {
if (['node_modules', '.git'].includes(file)) continue;
file = path.join(dir, file);
const stat = await fsp.lstat(file);
if (stat.isDirectory()) {
files = files.concat(await readDirDeep(file));
} else if (stat.isSymbolicLink()) {
files.push({ type: 'symbolic', path: file });
} else files.push({ type: 'file', path: file });
}
return files;
};

View File

@ -0,0 +1,67 @@
/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
const patches = {
"*": async (scriptId, scriptContent) => {
const prevTriggerFound = /require\(['|"]notion-enhancer['|"]\)/.test(scriptContent);
if (prevTriggerFound) return scriptContent;
const enhancerTrigger =
'\n\n/*notion-enhancer*/require("notion-enhancer")' +
`('${scriptId}',exports,(js)=>eval(js));`;
return scriptContent + enhancerTrigger;
},
"main/main": async (scriptContent) => {
// https://github.com/notion-enhancer/desktop/issues/160
// enable the notion:// url scheme/protocol on linux
const searchValue = /process.platform === "win32"/g,
replaceValue = 'process.platform === "win32" || process.platform === "linux"';
if (scriptContent.includes(replaceValue)) return scriptContent;
return scriptContent.replace(searchValue, replaceValue);
},
"main/schemeHandler": async (scriptContent) => {
// https://github.com/notion-enhancer/desktop/issues/291
// bypass csp issues by intercepting notion:// protocol
const searchValue =
"protocol.registerStreamProtocol(config_1.default.protocol, async (req, callback) => {",
replaceValue = `${searchValue}
{ /* notion-enhancer */
const schemePrefix = "notion://www.notion.so/__notion-enhancer/";
if (req.url.startsWith(schemePrefix)) {
const { search, hash, pathname } = new URL(req.url),
fileExt = pathname.split(".").reverse()[0],
filePath = \`../node_modules/notion-enhancer/\${req.url.slice(
schemePrefix.length,
-(search.length + hash.length)
)}\`,
mimeType = Object.entries(require("notion-enhancer/dep/mime-db.json"))
.filter(([_, data]) => data.extensions)
.find(([_, data]) => data.extensions.includes(fileExt));
callback({
data: require("fs").createReadStream(require("path").resolve(\`\${__dirname}/\${filePath}\`)),
headers: { "content-type": mimeType },
});
}
}`;
if (scriptContent.includes(replaceValue)) return scriptContent;
return scriptContent.replace(searchValue, replaceValue);
},
"main/systemMenu": async (scriptContent) => {
// exposes template for modification
const searchValue = "electron_1.Menu.setApplicationMenu(menu);",
replaceValue = `${searchValue} return template;`;
if (scriptContent.includes(replaceValue)) return scriptContent;
return scriptContent.replace(searchValue, replaceValue);
},
};
export default async (scriptId, scriptContent) => {
if (patches["*"]) scriptContent = await patches["*"](scriptId, scriptContent);
if (patches[scriptId]) scriptContent = await patches[scriptId](scriptContent);
return scriptContent;
};

View File

@ -1,48 +0,0 @@
/**
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
import fsp from 'fs/promises';
import { log, spinner, line } from './cli.mjs';
import { findNotion } from './helpers.mjs';
import check from './check.mjs';
export default async function (notionFolder = findNotion(), { delCache = undefined } = {}) {
const status = check(notionFolder);
let s;
if (status.code > 1 && status.executable) {
s = spinner(' * removing enhancements').loop();
await fsp.rm(status.executable, { recursive: true });
s.stop();
} else log` {grey * enhancements not found: skipping}`;
if (status.backup) {
s = spinner(' * restoring backup').loop();
await fsp.rename(status.backup, status.backup.replace(/\.bak$/, ''));
s.stop();
} else log` {grey * backup not found: skipping}`;
if (status.cache) {
log` * enhancer cache found: ${status.cache}`;
const prompt = ['Y', 'y', 'N', 'n', ''];
let res;
if (prompt.includes(delCache)) {
res = delCache;
log` {inverse > delete? [Y/n]:} ${delCache} {grey (auto-filled)}`;
} else res = await line.read(' {inverse > delete? [Y/n]:} ', prompt);
if (res.toLowerCase() === 'n') {
log` * keeping enhancer cache`;
} else {
s = spinner(' * deleting enhancer cache').loop();
await fsp.rm(status.cache, { recursive: true });
s.stop();
}
} else log` {grey * enhancer cache not found: skipping}`;
return true;
}

View File

@ -1,23 +0,0 @@
/**
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
import fsp from 'fs/promises';
export default async function (filepath) {
// https://github.com/notion-enhancer/desktop/issues/160
// enable the notion:// url scheme/protocol on linux
const contents = await fsp.readFile(filepath, 'utf8');
await fsp.writeFile(
filepath,
contents.replace(
/process.platform === "win32"/g,
'process.platform === "win32" || process.platform === "linux"'
)
);
return true;
}

View File

@ -1,45 +0,0 @@
/**
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
import fsp from 'fs/promises';
export default async function (filepath) {
// https://github.com/notion-enhancer/desktop/issues/291
// bypass csp issues by intercepting notion:// protocol
const contents = await fsp.readFile(filepath, 'utf8');
await fsp.writeFile(
filepath,
contents.replace(
/const success = protocol\.registerStreamProtocol\(config_1.default.protocol, async \(req, callback\) => \{/,
`const success = protocol.registerStreamProtocol(config_1.default.protocol, async (req, callback) => {
{
// notion-enhancer
const schemePrefix = 'notion://www.notion.so/__notion-enhancer/';
if (req.url.startsWith(schemePrefix)) {
const { search, hash, pathname } = new URL(req.url),
resolvePath = (path) => require('path').resolve(\`\${__dirname}/\${path}\`),
fileExt = pathname.split('.').reverse()[0],
mimeDB = Object.entries(require('notion-enhancer/dep/mime-db.json')),
mimeType = mimeDB
.filter(([mime, data]) => data.extensions)
.find(([mime, data]) => data.extensions.includes(fileExt));
let filePath = '../node_modules/notion-enhancer/';
filePath += req.url.slice(schemePrefix.length);
if (search) filePath = filePath.slice(0, -search.length);
if (hash) filePath = filePath.slice(0, -hash.length);
callback({
data: require('fs').createReadStream(resolvePath(filePath)),
headers: { 'content-type': mimeType },
});
}
}`
)
);
return true;
}

View File

@ -1,22 +0,0 @@
/**
* notion-enhancer
* (c) 2021 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
'use strict';
import fsp from 'fs/promises';
export default async function (filepath) {
// so that e.g. tabs access and modify the template
const contents = await fsp.readFile(filepath, 'utf8');
await fsp.writeFile(
filepath,
contents.replace(
/electron_1\.Menu\.setApplicationMenu\(menu\);/g,
'electron_1.Menu.setApplicationMenu(menu); return template;'
)
);
return true;
}