mirror of
https://github.com/notion-enhancer/notion-enhancer.git
synced 2025-04-12 00:09:03 +00:00
feat(cli): safer patch process to avoid repeat-patching
This commit is contained in:
parent
ebf15dbfb9
commit
979616e032
137
bin.mjs
137
bin.mjs
@ -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();
|
||||||
}
|
}
|
||||||
|
142
scripts/cli.mjs
142
scripts/cli.mjs
@ -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}`;
|
|
||||||
};
|
|
@ -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 () => {
|
||||||
|
@ -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;
|
|
||||||
};
|
|
67
scripts/patch-desktop-app.mjs
Normal file
67
scripts/patch-desktop-app.mjs
Normal 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;
|
||||||
|
};
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user