Skip to content

Commit

Permalink
refactor: identifier escapes + "self" group (Vendicated#339)
Browse files Browse the repository at this point in the history
Co-authored-by: Ven <[email protected]>
  • Loading branch information
Vap0r1ze and Vendicated authored Dec 19, 2022
1 parent 4974c53 commit 989bd36
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 30 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@
"no-extra-semi": "error",
"consistent-return": ["warn", { "treatUndefinedAsUnspecified": true }],
"dot-notation": "error",
"no-useless-escape": "error",
"no-useless-escape": ["error", {
"extra": "i"
}],
"no-fallthrough": "error",
"for-direction": "error",
"no-async-promise-executor": "error",
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@
"packageManager": "[email protected]",
"pnpm": {
"patchedDependencies": {
"[email protected]": "patches/[email protected]"
"[email protected]": "patches/[email protected]",
"[email protected]": "patches/[email protected]"
}
},
"webExt": {
Expand Down
45 changes: 45 additions & 0 deletions patches/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
diff --git a/lib/rules/no-useless-escape.js b/lib/rules/no-useless-escape.js
index 2046a148a17fd1d5f3a4bbc9f45f7700259d11fa..f4898c6b57355a4fd72c43a9f32bf1a36a6ccf4a 100644
--- a/lib/rules/no-useless-escape.js
+++ b/lib/rules/no-useless-escape.js
@@ -97,12 +97,30 @@ module.exports = {
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
},

- schema: []
+ schema: [{
+ type: "object",
+ properties: {
+ extra: {
+ type: "string",
+ default: ""
+ },
+ extraCharClass: {
+ type: "string",
+ default: ""
+ },
+ },
+ additionalProperties: false
+ }]
},

create(context) {
+ const options = context.options[0] || {};
+ const { extra, extraCharClass } = options || ''
const sourceCode = context.getSourceCode();

+ const NON_CHARCLASS_ESCAPES = union(REGEX_NON_CHARCLASS_ESCAPES, new Set(extra))
+ const CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set(extraCharClass))
+
/**
* Reports a node
* @param {ASTNode} node The node to report
@@ -238,7 +256,7 @@ module.exports = {
.filter(charInfo => charInfo.escaped)

// Filter out characters that are valid to escape, based on their position in the regular expression.
- .filter(charInfo => !(charInfo.inCharClass ? REGEX_GENERAL_ESCAPES : REGEX_NON_CHARCLASS_ESCAPES).has(charInfo.text))
+ .filter(charInfo => !(charInfo.inCharClass ? CHARCLASS_ESCAPES : NON_CHARCLASS_ESCAPES).has(charInfo.text))

// Report all the remaining characters.
.forEach(charInfo => report(node, charInfo.index, charInfo.text));
26 changes: 15 additions & 11 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 17 additions & 6 deletions src/components/PatchHelper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import { debounce } from "@utils/debounce";
import { makeCodeblock } from "@utils/misc";
import { canonicalizeMatch, canonicalizeReplace, ReplaceFn } from "@utils/patches";
import { search } from "@webpack";
import { Button, Clipboard, Forms, Margins, Parser, React, Switch, Text, TextInput } from "@webpack/common";

Expand All @@ -41,20 +42,29 @@ const findCandidates = debounce(function ({ find, setModule, setError }) {
setModule([keys[0], candidates[keys[0]]]);
});

function ReplacementComponent({ module, match, replacement, setReplacementError }) {
interface ReplacementComponentProps {
module: [id: number, factory: Function];
match: string | RegExp;
replacement: string | ReplaceFn;
setReplacementError(error: any): void;
}

function ReplacementComponent({ module, match, replacement, setReplacementError }: ReplacementComponentProps) {
const [id, fact] = module;
const [compileResult, setCompileResult] = React.useState<[boolean, string]>();

const [patchedCode, matchResult, diff] = React.useMemo(() => {
const src: string = fact.toString().replaceAll("\n", "");
const canonicalMatch = canonicalizeMatch(match);
try {
var patched = src.replace(match, replacement);
const canonicalReplace = canonicalizeReplace(replacement, "YourPlugin");
var patched = src.replace(canonicalMatch, canonicalReplace as string);
setReplacementError(void 0);
} catch (e) {
setReplacementError((e as Error).message);
return ["", [], []];
}
const m = src.match(match);
const m = src.match(canonicalMatch);
return [patched, m, makeDiff(src, patched, m)];
}, [id, match, replacement]);

Expand Down Expand Up @@ -179,9 +189,10 @@ function ReplacementInput({ replacement, setReplacement, replacementError }) {
{Object.entries({
"$$": "Insert a $",
"$&": "Insert the entire match",
"$`": "Insert the substring before the match",
"$`\u200b": "Insert the substring before the match",
"$'": "Insert the substring after the match",
"$n": "Insert the nth capturing group ($1, $2...)"
"$n": "Insert the nth capturing group ($1, $2...)",
"$self": "Insert the plugin instance",
}).map(([placeholder, desc]) => (
<Forms.FormText key={placeholder}>
{Parser.parse("`" + placeholder + "`")}: {desc}
Expand All @@ -206,7 +217,7 @@ function ReplacementInput({ replacement, setReplacement, replacementError }) {
function PatchHelper() {
const [find, setFind] = React.useState<string>("");
const [match, setMatch] = React.useState<string>("");
const [replacement, setReplacement] = React.useState<string | Function>("");
const [replacement, setReplacement] = React.useState<string | ReplaceFn>("");

const [replacementError, setReplacementError] = React.useState<string>();

Expand Down
6 changes: 3 additions & 3 deletions src/plugins/shikiCodeblocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { wordsFromPascal, wordsToTitle } from "@utils/text";
import definePlugin, { OptionType } from "@utils/types";

import previewExampleText from "~fileContent/previewExample.tsx";
import cssText from "~fileContent/style.css";
import cssText from "~fileContent/shiki.css";

import { Settings } from "../../Vencord";
import { shiki } from "./api/shiki";
Expand All @@ -44,8 +44,8 @@ export default definePlugin({
{
find: "codeBlock:{react:function",
replacement: {
match: /codeBlock:\{react:function\((.),(.),(.)\)\{/,
replace: "$&return Vencord.Plugins.plugins.ShikiCodeblocks.renderHighlighter($1,$2,$3);",
match: /codeBlock:\{react:function\((\i),(\i),(\i)\)\{/,
replace: "$&return $self.renderHighlighter($1,$2,$3);",
},
},
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
.shiki-root {
border-radius: 4px;

.shiki-container {
border: 4px;
/* fallback background */
background-color: var(--background-secondary);
}

.shiki-root {
border-radius: 4px;
}

.shiki-root code {
display: block;
overflow-x: auto;
Expand Down
55 changes: 55 additions & 0 deletions src/utils/patches.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import { PatchReplacement } from "./types";

export type ReplaceFn = (match: string, ...groups: string[]) => string;

export function canonicalizeMatch(match: RegExp | string) {
if (typeof match === "string") return match;
const canonSource = match.source
.replaceAll("\\i", "[A-Za-z_$][\\w$]*");
return new RegExp(canonSource, match.flags);
}

export function canonicalizeReplace(replace: string | ReplaceFn, pluginName: string) {
if (typeof replace === "function") return replace;
return replace.replaceAll("$self", `Vencord.Plugins.plugins.${pluginName}`);
}

export function canonicalizeDescriptor<T>(descriptor: TypedPropertyDescriptor<T>, canonicalize: (value: T) => T) {
if (descriptor.get) {
const original = descriptor.get;
descriptor.get = function () {
return canonicalize(original.call(this));
};
} else if (descriptor.value) {
descriptor.value = canonicalize(descriptor.value);
}
return descriptor;
}

export function canonicalizeReplacement(replacement: Pick<PatchReplacement, "match" | "replace">, plugin: string) {
const descriptors = Object.getOwnPropertyDescriptors(replacement);
descriptors.match = canonicalizeDescriptor(descriptors.match, canonicalizeMatch);
descriptors.replace = canonicalizeDescriptor(
descriptors.replace,
replace => canonicalizeReplace(replace, plugin),
);
Object.defineProperties(replacement, descriptors);
}
4 changes: 3 additions & 1 deletion src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
import { Command } from "@api/Commands";
import { Promisable } from "type-fest";

import type { ReplaceFn } from "./patches";

// exists to export default definePlugin({...})
export default function definePlugin<P extends PluginDef>(p: P & Record<string, any>) {
return p;
}

export interface PatchReplacement {
match: string | RegExp;
replace: string | ((match: string, ...groups: string[]) => string);
replace: string | ReplaceFn;
predicate?(): boolean;
}

Expand Down
12 changes: 8 additions & 4 deletions src/webpack/patchWebpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import { WEBPACK_CHUNK } from "@utils/constants";
import Logger from "@utils/Logger";
import { canonicalizeReplacement } from "@utils/patches";
import { PatchReplacement } from "@utils/types";

import { _initWebpack } from ".";

Expand Down Expand Up @@ -135,15 +137,17 @@ function patchPush() {
if (code.includes(patch.find)) {
patchedBy.add(patch.plugin);

// @ts-ignore we change all patch.replacement to array in plugins/index
for (const replacement of patch.replacement) {
// we change all patch.replacement to array in plugins/index
for (const replacement of patch.replacement as PatchReplacement[]) {
if (replacement.predicate && !replacement.predicate()) continue;
const lastMod = mod;
const lastCode = code;

canonicalizeReplacement(replacement, patch.plugin);

try {
const newCode = code.replace(replacement.match, replacement.replace);
if (newCode === code && !replacement.noWarn) {
const newCode = code.replace(replacement.match, replacement.replace as string);
if (newCode === code && !patch.noWarn) {
logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`);
if (IS_DEV) {
logger.debug("Function Source:\n", code);
Expand Down

0 comments on commit 989bd36

Please sign in to comment.