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;
// prevent line clear remove existing stdout
print`\n`;
do {
// 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();
} while (!values.includes(input));
} while (!["Y", "y", "N", "n"].includes(input));
return input;
};
@ -89,13 +89,13 @@ const commands = [
// ["alias, option=example", [type, "description"]]
[
"--path=</path/to/notion/resources>",
[String, "provide notion installation location (defaults to auto-detected)"],
[String, "manually provide a notion installation location"],
],
["--backup", [Boolean, ""]],
["--overwrite", [Boolean, ""]],
["--overwrite", [Boolean, "overwrite inserted enhancements (for rapid development)"]],
["-y, --yes", [Boolean, 'skip prompts; assume "yes" 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"]],
["-j, --json", [Boolean, "display json output (where applicable)"]],
["-h, --help", [Boolean, "display usage information"]],
@ -174,7 +174,6 @@ const args = arg(compileOptsToArgSpec(options)),
print`${enhancerVersion} via ${nodeVersion} on ${osVersion}\n`;
}
};
if (args["--quiet"]) __quiet = true;
if (args["--help"]) [printHelp(), process.exit()];
if (args["--version"]) [printVersion(), process.exit()];
@ -185,34 +184,20 @@ const defaultPromptValue = args["--yes"]
? "n"
: undefined;
const notionNotFound = `notion installation not found (corrupted or nonexistent)`,
enhancerNotApplied = `notion-enhancer not applied (notion installation found)`,
const appPath = getAppPath(),
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 ✔}`,
onFail = chalk`{bold.whiteBright FAILURE} {red ✘}`,
onCancel = chalk`{bold.whiteBright CANCELLED} {red ✘}`;
switch (args["_"][0]) {
case "apply": {
print`{bold.whiteBright [NOTION-ENHANCER] APPLY} `;
// 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`;
const removeEnhancementsVerbose = async () => {
if (appPath) {
print` {grey * ${onNotionNotFound}: skipping}\n`;
} else if (insertVersion) {
print` {grey * notion installation found: notion-enhancer v${insertVersion} applied}\n`;
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 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}`;
return false;
}
} else print` {grey * ${enhancerNotApplied}: skipping}\n`;
} else print` {grey * ${onEnhancerNotApplied}: skipping}\n`;
return true;
},
removeCacheVerbose = async () => {
// optionally remove ~/.notion-enhancer
if (existsSync(cachePath)) {
if (!existsSync(cachePath)) {
print` {grey * cache found: ${cachePath}}\n`;
let deleteCache;
const prompt = chalk` {inverse > delete? [Y/n]:} `;
if (defaultPromptValue) {
deleteCache = defaultPromptValue;
print`${prompt}${defaultPromptValue}\n`;
} else deleteCache = await promptForValue(prompt, ["Y", "y", "N", "n"]);
const deleteCache = defaultPromptValue ?? (await promptInput("delete?"));
if (defaultPromptValue) print` {inverse > delete? [Y/n]:} ${deleteCache}\n`;
if (["Y", "y"].includes(deleteCache)) {
print` {grey * cache found: removing}`;
startSpinner();
@ -243,15 +228,65 @@ switch (args["_"][0]) {
stopSpinner();
} else print` {grey * cache found: keeping}\n`;
} else print` {grey * cache not found: skipping}\n`;
// failure if backup could not be restored
print`${insertVersion && !backupPath ? onFail : onSuccess}\n`;
return true;
};
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;
}
case "check":
const appPath = getAppPath(),
backupPath = getBackupPath(),
cachePath = getCachePath(),
insertVersion = checkEnhancementVersion();
case "remove": {
print`{bold.whiteBright [NOTION-ENHANCER] REMOVE}\n`;
let success = await removeEnhancementsVerbose();
success = (await removeCacheVerbose()) && success;
// failure if backup could not be restored
print`${success ? onSuccess : onFail}\n`;
break;
}
case "check": {
if (args["--json"]) {
printObject({
appPath,
@ -264,13 +299,15 @@ switch (args["_"][0]) {
}
print`{bold.whiteBright [NOTION-ENHANCER] CHECK:} `;
if (manifest.version === insertVersion) {
print`notion-enhancer v${manifest.version} applied\n`;
print`notion-enhancer v${insertVersion} applied\n`;
} else if (insertVersion) {
print`notion-enhancer v${manifest.version} applied != v${insertVersion} cli\n`;
print`${onVersionMismatch}\n`;
} else if (appPath) {
print`${enhancerNotApplied}.\n`;
} else print`${notionNotFound}.\n`;
print`${onEnhancerNotApplied}\n`;
} else print`${onNotionNotFound}\n`;
break;
}
default:
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 os from "node:os";
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 { createRequire } from "node:module";
import patch from "./patch-desktop-app.mjs";
let __notionResources, __enhancerCache;
const nodeRequire = createRequire(import.meta.url),
manifest = nodeRequire("../package.json"),
platform =
process.platform === "linux" && os.release().toLowerCase().includes("microsoft")
? "wsl"
@ -39,6 +43,21 @@ const nodeRequire = createRequire(import.meta.url),
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) => {
@ -78,8 +97,8 @@ const setNotionPath = (path) => {
checkEnhancementVersion = () => {
const insertPath = getResourcePath("app/node_modules/notion-enhancer");
if (!existsSync(insertPath)) return undefined;
const insertManifest = getResourcePath("app/node_modules/notion-enhancer/package.json"),
insertVersion = nodeRequire(insertManifest).version;
const manifestPath = getResourcePath("app/node_modules/notion-enhancer/package.json"),
insertVersion = nodeRequire(manifestPath).version;
return insertVersion;
};
@ -89,10 +108,42 @@ const unpackApp = () => {
asar.extractAll(appPath, appPath.replace(/\.asar$/, ""));
return true;
},
applyEnhancements = () => {
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,
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;
},
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;
}