@plexcord-companion/plexcord-ast-parser 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PlexcordAstParser.d.ts +53 -0
- package/dist/PlexcordAstParser.js +325 -0
- package/dist/PlexcordAstParser.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +5 -0
- package/dist/util.js +42 -0
- package/dist/util.js.map +1 -0
- package/package.json +34 -0
- package/src/PlexcordAstParser.ts +429 -0
- package/src/index.ts +3 -0
- package/src/types.ts +64 -0
- package/src/util.ts +71 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type Expression, type Identifier, type Node, type ObjectLiteralExpression } from "typescript";
|
|
2
|
+
import { AstParser, type Import } from "@plexcord-companion/ast-parser";
|
|
3
|
+
import { type Logger } from "@plexcord-companion/shared/Logger";
|
|
4
|
+
import type { FindUse, FunctionNode, IFindType, IReplacement, PatchData, RegexNode, SourcePatch, StringNode } from "./types.js";
|
|
5
|
+
export declare function setLogger(newLogger: Logger): void;
|
|
6
|
+
export declare class PlexcordAstParser extends AstParser {
|
|
7
|
+
private readonly _path;
|
|
8
|
+
get path(): string;
|
|
9
|
+
/**
|
|
10
|
+
* @CacheGetter
|
|
11
|
+
*/
|
|
12
|
+
get imports(): Map<Identifier, Import>;
|
|
13
|
+
constructor(content: string, path: string);
|
|
14
|
+
/**
|
|
15
|
+
* @Cache
|
|
16
|
+
*/
|
|
17
|
+
private findDefinePlugin;
|
|
18
|
+
getPluginName(): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* @Cache
|
|
21
|
+
*/
|
|
22
|
+
getPatches(): SourcePatch[];
|
|
23
|
+
parseFind(patch: ObjectLiteralExpression): IFindType | null;
|
|
24
|
+
/**
|
|
25
|
+
* Try to parse a string literal
|
|
26
|
+
*
|
|
27
|
+
* if it is a template literal, attempt to extract the string content by inlining variables
|
|
28
|
+
*/
|
|
29
|
+
tryParseStringLiteral(node: Node): string | null;
|
|
30
|
+
tryParseStringLiteralToStringNode(node: Node): StringNode | null;
|
|
31
|
+
tryParseFunction(node: Node): FunctionNode | null;
|
|
32
|
+
parseReplace(node: Expression): StringNode | FunctionNode | null;
|
|
33
|
+
parseMatch(node: Expression): StringNode | RegexNode | null;
|
|
34
|
+
parseReplacement(patch: ObjectLiteralExpression): IReplacement[] | null;
|
|
35
|
+
parsePatch(patch: ObjectLiteralExpression): PatchData | null;
|
|
36
|
+
/**
|
|
37
|
+
* @returns true if this file is the entry point (if it has a definePlugin call)
|
|
38
|
+
*/
|
|
39
|
+
isRootPluginFile(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* @Cache
|
|
42
|
+
*/
|
|
43
|
+
getFinds(): FindUse[];
|
|
44
|
+
/**
|
|
45
|
+
* @Cache
|
|
46
|
+
*/
|
|
47
|
+
private getFindUses;
|
|
48
|
+
/**
|
|
49
|
+
* returns the import if this identifier is imported
|
|
50
|
+
*/
|
|
51
|
+
private isIdentifierImported;
|
|
52
|
+
private listImports;
|
|
53
|
+
}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { DeclarationDomain } from "ts-api-utils";
|
|
8
|
+
import { createPrinter, EmitHint, isArrayLiteralExpression, isArrowFunction, isCallExpression, isFunctionExpression, isIdentifier, isNamespaceImport, isObjectLiteralExpression, isPropertyAssignment, isRegularExpressionLiteral, isStringLiteral, isStringLiteralLike, isTemplateExpression, isVariableDeclaration, ScriptTarget, transpileModule, } from "typescript";
|
|
9
|
+
import { AstParser, findObjectLiteralByKey, findParent, getImportName, getImportSource, isDefaultImport, isInImportStatment, } from "@plexcord-companion/ast-parser";
|
|
10
|
+
import { Cache, CacheGetter } from "@plexcord-companion/shared/decorators";
|
|
11
|
+
import { NoopLogger } from "@plexcord-companion/shared/Logger";
|
|
12
|
+
import { tryParseRegularExpressionLiteral } from "./util.js";
|
|
13
|
+
let logger = NoopLogger;
|
|
14
|
+
export function setLogger(newLogger) {
|
|
15
|
+
logger = newLogger;
|
|
16
|
+
}
|
|
17
|
+
export class PlexcordAstParser extends AstParser {
|
|
18
|
+
_path;
|
|
19
|
+
get path() {
|
|
20
|
+
return this._path;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @CacheGetter
|
|
24
|
+
*/
|
|
25
|
+
get imports() {
|
|
26
|
+
return this.listImports();
|
|
27
|
+
}
|
|
28
|
+
constructor(content, path) {
|
|
29
|
+
super(content);
|
|
30
|
+
this._path = path;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* @Cache
|
|
34
|
+
*/
|
|
35
|
+
findDefinePlugin() {
|
|
36
|
+
const define = [...this.imports.values()].find((x) => {
|
|
37
|
+
if (!x.default)
|
|
38
|
+
return;
|
|
39
|
+
if (x.source !== "@utils/types")
|
|
40
|
+
return;
|
|
41
|
+
return true;
|
|
42
|
+
});
|
|
43
|
+
if (!define)
|
|
44
|
+
return;
|
|
45
|
+
const uses = this.vars.get(define.as)?.uses;
|
|
46
|
+
if (!uses)
|
|
47
|
+
return;
|
|
48
|
+
const definePlugin = uses.find(({ location }) => {
|
|
49
|
+
if (!isCallExpression(location.parent))
|
|
50
|
+
return;
|
|
51
|
+
return location.parent.arguments.length === 1 && isObjectLiteralExpression(location.parent.arguments[0]);
|
|
52
|
+
});
|
|
53
|
+
return (definePlugin?.location.parent).arguments[0];
|
|
54
|
+
}
|
|
55
|
+
// TODO: work on files in the plugin folder but not the root plugin file
|
|
56
|
+
getPluginName() {
|
|
57
|
+
const definePlugin = this.findDefinePlugin();
|
|
58
|
+
if (!definePlugin)
|
|
59
|
+
return null;
|
|
60
|
+
const nameProp = findObjectLiteralByKey(definePlugin, "name");
|
|
61
|
+
if (!nameProp || !isPropertyAssignment(nameProp) || !isStringLiteral(nameProp.initializer))
|
|
62
|
+
return null;
|
|
63
|
+
return nameProp.initializer.text;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* @Cache
|
|
67
|
+
*/
|
|
68
|
+
getPatches() {
|
|
69
|
+
const definePlugin = this.findDefinePlugin();
|
|
70
|
+
if (!definePlugin)
|
|
71
|
+
return [];
|
|
72
|
+
const patchesProp = findObjectLiteralByKey(definePlugin, "patches");
|
|
73
|
+
if (!patchesProp || !isPropertyAssignment(patchesProp) || !isArrayLiteralExpression(patchesProp.initializer))
|
|
74
|
+
return [];
|
|
75
|
+
return patchesProp.initializer.elements
|
|
76
|
+
.map((x, origIndex) => {
|
|
77
|
+
if (!isObjectLiteralExpression(x))
|
|
78
|
+
return null;
|
|
79
|
+
const res = this.parsePatch(x);
|
|
80
|
+
if (!res)
|
|
81
|
+
return null;
|
|
82
|
+
return {
|
|
83
|
+
...res,
|
|
84
|
+
range: this.makeRangeFromAstNode(x.getChildAt(1)),
|
|
85
|
+
origIndex,
|
|
86
|
+
};
|
|
87
|
+
})
|
|
88
|
+
.filter((x) => x !== null);
|
|
89
|
+
}
|
|
90
|
+
parseFind(patch) {
|
|
91
|
+
const find = findObjectLiteralByKey(patch, "find");
|
|
92
|
+
if (!find || !isPropertyAssignment(find))
|
|
93
|
+
return null;
|
|
94
|
+
if (!(isStringLiteral(find.initializer) || isRegularExpressionLiteral(find.initializer)))
|
|
95
|
+
return null;
|
|
96
|
+
return {
|
|
97
|
+
findType: isStringLiteral(find.initializer) ? "string" : "regex",
|
|
98
|
+
find: find.initializer.text,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Try to parse a string literal
|
|
103
|
+
*
|
|
104
|
+
* if it is a template literal, attempt to extract the string content by inlining variables
|
|
105
|
+
*/
|
|
106
|
+
tryParseStringLiteral(node) {
|
|
107
|
+
tryParse: if (isStringLiteralLike(node)) {
|
|
108
|
+
return node.text;
|
|
109
|
+
// resolve template literals if they are constant
|
|
110
|
+
}
|
|
111
|
+
else if (isTemplateExpression(node)) {
|
|
112
|
+
const resolvedSpans = [];
|
|
113
|
+
for (const span of node.templateSpans) {
|
|
114
|
+
const spanExpr = span.expression;
|
|
115
|
+
if (!isIdentifier(spanExpr)) {
|
|
116
|
+
logger.debug(`[PlexcordAstParser] Trying to parse template literal with non-identifier span: ${span.getText()}, FileName: ${span.getSourceFile().fileName}`);
|
|
117
|
+
break tryParse;
|
|
118
|
+
}
|
|
119
|
+
const usageInfo = this.getVarInfoFromUse(spanExpr);
|
|
120
|
+
if (!usageInfo) {
|
|
121
|
+
break tryParse;
|
|
122
|
+
}
|
|
123
|
+
else if (usageInfo.declarations.length === 0) {
|
|
124
|
+
logger.trace(`[PlexcordAstParser] Could not resolve identifier ${spanExpr.text} to a variable declaration. Is it a global?`);
|
|
125
|
+
break tryParse;
|
|
126
|
+
}
|
|
127
|
+
const isConst = this.isConstDeclared(usageInfo);
|
|
128
|
+
if (!isConst) {
|
|
129
|
+
break tryParse;
|
|
130
|
+
}
|
|
131
|
+
const decl = findParent(isConst[0], isVariableDeclaration);
|
|
132
|
+
if (!decl) {
|
|
133
|
+
break tryParse;
|
|
134
|
+
}
|
|
135
|
+
const init = decl.initializer;
|
|
136
|
+
if (!init) {
|
|
137
|
+
break tryParse;
|
|
138
|
+
}
|
|
139
|
+
const initValue = this.tryParseStringLiteral(init);
|
|
140
|
+
// explicitly check for null to avoid empty string
|
|
141
|
+
if (initValue == null) {
|
|
142
|
+
break tryParse;
|
|
143
|
+
}
|
|
144
|
+
resolvedSpans.push(initValue + span.literal.text);
|
|
145
|
+
}
|
|
146
|
+
return node.head.text + resolvedSpans.join("");
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
tryParseStringLiteralToStringNode(node) {
|
|
151
|
+
const str = this.tryParseStringLiteral(node);
|
|
152
|
+
if (str == null)
|
|
153
|
+
return null;
|
|
154
|
+
return {
|
|
155
|
+
type: "string",
|
|
156
|
+
value: str,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
tryParseFunction(node) {
|
|
160
|
+
if (!isArrowFunction(node) && !isFunctionExpression(node))
|
|
161
|
+
return null;
|
|
162
|
+
const code = createPrinter()
|
|
163
|
+
.printNode(EmitHint.Expression, node, node.getSourceFile());
|
|
164
|
+
const res = transpileModule(code, {
|
|
165
|
+
compilerOptions: {
|
|
166
|
+
target: ScriptTarget.ESNext,
|
|
167
|
+
strict: true,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
if (res.diagnostics && res.diagnostics.length > 0)
|
|
171
|
+
return null;
|
|
172
|
+
return {
|
|
173
|
+
type: "function",
|
|
174
|
+
value: res.outputText,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
parseReplace(node) {
|
|
178
|
+
return this.tryParseStringLiteralToStringNode(node) ?? this.tryParseFunction(node);
|
|
179
|
+
}
|
|
180
|
+
parseMatch(node) {
|
|
181
|
+
return this.tryParseStringLiteralToStringNode(node) ?? tryParseRegularExpressionLiteral(node);
|
|
182
|
+
}
|
|
183
|
+
parseReplacement(patch) {
|
|
184
|
+
const replacementObj = findObjectLiteralByKey(patch, "replacement");
|
|
185
|
+
if (!replacementObj || !isPropertyAssignment(replacementObj))
|
|
186
|
+
return null;
|
|
187
|
+
const replacement = replacementObj.initializer;
|
|
188
|
+
const replacements = isArrayLiteralExpression(replacement) ? replacement.elements : [replacement];
|
|
189
|
+
if (!replacements.every(isObjectLiteralExpression))
|
|
190
|
+
return null;
|
|
191
|
+
const replacementValues = replacements.map((r) => {
|
|
192
|
+
const match = findObjectLiteralByKey(r, "match");
|
|
193
|
+
const replace = findObjectLiteralByKey(r, "replace");
|
|
194
|
+
if (!replace || !isPropertyAssignment(replace) || !match || !isPropertyAssignment(match))
|
|
195
|
+
return null;
|
|
196
|
+
const matchValue = this.parseMatch(match.initializer);
|
|
197
|
+
if (!matchValue)
|
|
198
|
+
return null;
|
|
199
|
+
const replaceValue = this.parseReplace(replace.initializer);
|
|
200
|
+
if (replaceValue == null)
|
|
201
|
+
return null;
|
|
202
|
+
return {
|
|
203
|
+
match: matchValue,
|
|
204
|
+
replace: replaceValue,
|
|
205
|
+
};
|
|
206
|
+
})
|
|
207
|
+
.filter((x) => x != null);
|
|
208
|
+
return replacementValues.length > 0 ? replacementValues : null;
|
|
209
|
+
}
|
|
210
|
+
parsePatch(patch) {
|
|
211
|
+
const find = this.parseFind(patch);
|
|
212
|
+
const replacement = this.parseReplacement(patch);
|
|
213
|
+
if (!replacement || !find)
|
|
214
|
+
return null;
|
|
215
|
+
return {
|
|
216
|
+
...find,
|
|
217
|
+
replacement,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* @returns true if this file is the entry point (if it has a definePlugin call)
|
|
222
|
+
*/
|
|
223
|
+
isRootPluginFile() {
|
|
224
|
+
return !!this.findDefinePlugin();
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* @Cache
|
|
228
|
+
*/
|
|
229
|
+
getFinds() {
|
|
230
|
+
return this.getFindUses()
|
|
231
|
+
.map((x) => {
|
|
232
|
+
const call = x.parent;
|
|
233
|
+
if (call.arguments.length === 0)
|
|
234
|
+
return false;
|
|
235
|
+
const args = call.arguments.map((x) => {
|
|
236
|
+
return this.tryParseStringLiteralToStringNode(x)
|
|
237
|
+
?? tryParseRegularExpressionLiteral(x)
|
|
238
|
+
?? this.tryParseFunction(x);
|
|
239
|
+
});
|
|
240
|
+
const range = this.makeRangeFromAstNode(call);
|
|
241
|
+
return {
|
|
242
|
+
range,
|
|
243
|
+
use: {
|
|
244
|
+
type: "testFind",
|
|
245
|
+
data: {
|
|
246
|
+
type: x.getText(),
|
|
247
|
+
args: args.filter((x) => x != null),
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
})
|
|
252
|
+
.filter((x) => x !== false);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* @Cache
|
|
256
|
+
*/
|
|
257
|
+
getFindUses() {
|
|
258
|
+
const imports = [...this.imports.entries()].flatMap(([k, v]) => {
|
|
259
|
+
if (v.source !== "@webpack")
|
|
260
|
+
return [];
|
|
261
|
+
const origName = (v.orig ?? v.as).getText();
|
|
262
|
+
if (!origName.startsWith("find"))
|
|
263
|
+
return [];
|
|
264
|
+
return k;
|
|
265
|
+
});
|
|
266
|
+
const toRet = [];
|
|
267
|
+
for (const i of imports) {
|
|
268
|
+
const uses = this.vars.get(i)?.uses;
|
|
269
|
+
if (!uses)
|
|
270
|
+
continue;
|
|
271
|
+
for (const { location } of uses) {
|
|
272
|
+
if (!isCallExpression(location.parent))
|
|
273
|
+
continue;
|
|
274
|
+
toRet.push(location);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return toRet;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* returns the import if this identifier is imported
|
|
281
|
+
*/
|
|
282
|
+
isIdentifierImported(i) {
|
|
283
|
+
const { declarations, domain } = this.vars.get(i) ?? {};
|
|
284
|
+
if (!declarations || declarations.length === 0)
|
|
285
|
+
return;
|
|
286
|
+
if (!(domain & DeclarationDomain.Import))
|
|
287
|
+
return;
|
|
288
|
+
const source = declarations.flatMap((x) => {
|
|
289
|
+
if (!isInImportStatment(x))
|
|
290
|
+
return [];
|
|
291
|
+
return x;
|
|
292
|
+
});
|
|
293
|
+
if (source.length !== 1)
|
|
294
|
+
return;
|
|
295
|
+
const [importIdent] = source;
|
|
296
|
+
return {
|
|
297
|
+
default: isDefaultImport(importIdent),
|
|
298
|
+
namespace: isNamespaceImport(importIdent),
|
|
299
|
+
...getImportName(importIdent),
|
|
300
|
+
source: getImportSource(importIdent),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
listImports() {
|
|
304
|
+
return new Map([...this.vars.entries()].flatMap(([k]) => {
|
|
305
|
+
const ret = this.isIdentifierImported(k);
|
|
306
|
+
return ret ? [[k, ret]] : [];
|
|
307
|
+
}));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
__decorate([
|
|
311
|
+
CacheGetter()
|
|
312
|
+
], PlexcordAstParser.prototype, "imports", null);
|
|
313
|
+
__decorate([
|
|
314
|
+
Cache()
|
|
315
|
+
], PlexcordAstParser.prototype, "findDefinePlugin", null);
|
|
316
|
+
__decorate([
|
|
317
|
+
Cache()
|
|
318
|
+
], PlexcordAstParser.prototype, "getPatches", null);
|
|
319
|
+
__decorate([
|
|
320
|
+
Cache()
|
|
321
|
+
], PlexcordAstParser.prototype, "getFinds", null);
|
|
322
|
+
__decorate([
|
|
323
|
+
Cache()
|
|
324
|
+
], PlexcordAstParser.prototype, "getFindUses", null);
|
|
325
|
+
//# sourceMappingURL=PlexcordAstParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlexcordAstParser.js","sourceRoot":"","sources":["../src/PlexcordAstParser.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAEH,aAAa,EACb,QAAQ,EAGR,wBAAwB,EACxB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,EAC1B,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EAGrB,YAAY,EACZ,eAAe,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACH,SAAS,EACT,sBAAsB,EACtB,UAAU,EACV,aAAa,EACb,eAAe,EAEf,eAAe,EACf,kBAAkB,GAErB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAe,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAG5E,OAAO,EAAE,gCAAgC,EAAE,MAAM,QAAQ,CAAC;AAG1D,IAAI,MAAM,GAAW,UAAU,CAAC;AAEhC,MAAM,UAAU,SAAS,CAAC,SAAiB;IACvC,MAAM,GAAG,SAAS,CAAC;AACvB,CAAC;AAED,MAAM,OAAO,iBAAkB,SAAQ,SAAS;IAC3B,KAAK,CAAS;IAE/B,IAAW,IAAI;QACX,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED;;OAEG;IAEH,IAAW,OAAO;QACd,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC9B,CAAC;IAED,YAAY,OAAe,EAAE,IAAY;QACrC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACtB,CAAC;IAED;;OAEG;IAEK,gBAAgB;QACpB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACjD,IAAI,CAAC,CAAC,CAAC,OAAO;gBACV,OAAO;YACX,IAAI,CAAC,CAAC,MAAM,KAAK,cAAc;gBAC3B,OAAO;YACX,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM;YACP,OAAO;QAEX,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;QAE5C,IAAI,CAAC,IAAI;YACL,OAAO;QAEX,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;YAC5C,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAClC,OAAO;YACX,OAAO,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,yBAAyB,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7G,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAyB,CAAA,CAAC,SAAS,CAAC,CAAC,CAA4B,CAAC;IACrG,CAAC;IAED,wEAAwE;IACjE,aAAa;QAChB,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE7C,IAAI,CAAC,YAAY;YACb,OAAO,IAAI,CAAC;QAEhB,MAAM,QAAQ,GAAG,sBAAsB,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAE9D,IAAI,CAAC,QAAQ,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,WAAW,CAAC;YACtF,OAAO,IAAI,CAAC;QAChB,OAAO,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC;IACrC,CAAC;IAED;;OAEG;IAEI,UAAU;QACb,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE7C,IAAI,CAAC,YAAY;YACb,OAAO,EAAE,CAAC;QAEd,MAAM,WAAW,GAAG,sBAAsB,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAEpE,IAAI,CAAC,WAAW,IAAI,CAAC,oBAAoB,CAAC,WAAW,CAAC,IAAI,CAAC,wBAAwB,CAAC,WAAW,CAAC,WAAW,CAAC;YACxG,OAAO,EAAE,CAAC;QACd,OAAO,WAAW,CAAC,WAAW,CAAC,QAAQ;aAClC,GAAG,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE;YAClB,IAAI,CAAC,yBAAyB,CAAC,CAAC,CAAC;gBAC7B,OAAO,IAAI,CAAC;YAEhB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAE/B,IAAI,CAAC,GAAG;gBACJ,OAAO,IAAI,CAAC;YAChB,OAAO;gBACH,GAAG,GAAG;gBACN,KAAK,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBACjD,SAAS;aACZ,CAAC;QACN,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,SAAS,CAAC,KAA8B;QACpC,MAAM,IAAI,GAAG,sBAAsB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAEnD,IAAI,CAAC,IAAI,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC;YACpC,OAAO,IAAI,CAAC;QAChB,IAAI,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,0BAA0B,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACpF,OAAO,IAAI,CAAC;QAEhB,OAAO;YACH,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;YAChE,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI;SAC9B,CAAC;IACN,CAAC;IAED;;;;OAIG;IACH,qBAAqB,CAAC,IAAU;QAC5B,QAAQ,EAAE,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC,IAAI,CAAC;YACjB,iDAAiD;QACrD,CAAC;aAAM,IAAI,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,aAAa,GAAG,EAAc,CAAC;YAErC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC;gBAEjC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC1B,MAAM,CAAC,KAAK,CAAC,kFAAkF,IAAI,CAAC,OAAO,EAAE,eAAe,IAAI,CAAC,aAAa,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;oBAC7J,MAAM,QAAQ,CAAC;gBACnB,CAAC;gBAED,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;gBAEnD,IAAI,CAAC,SAAS,EAAE,CAAC;oBACb,MAAM,QAAQ,CAAC;gBACnB,CAAC;qBAAM,IAAI,SAAS,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC7C,MAAM,CAAC,KAAK,CAAC,oDAAoD,QAAQ,CAAC,IAAI,6CAA6C,CAAC,CAAC;oBAC7H,MAAM,QAAQ,CAAC;gBACnB,CAAC;gBAED,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;gBAEhD,IAAI,CAAC,OAAO,EAAE,CAAC;oBACX,MAAM,QAAQ,CAAC;gBACnB,CAAC;gBAED,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC;gBAE3D,IAAI,CAAC,IAAI,EAAE,CAAC;oBACR,MAAM,QAAQ,CAAC;gBACnB,CAAC;gBAED,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;gBAE9B,IAAI,CAAC,IAAI,EAAE,CAAC;oBACR,MAAM,QAAQ,CAAC;gBACnB,CAAC;gBAED,MAAM,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;gBAEnD,kDAAkD;gBAClD,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;oBACpB,MAAM,QAAQ,CAAC;gBACnB,CAAC;gBAED,aAAa,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACtD,CAAC;YAED,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,iCAAiC,CAAC,IAAU;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAE7C,IAAI,GAAG,IAAI,IAAI;YACX,OAAO,IAAI,CAAC;QAEhB,OAAO;YACH,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,GAAG;SACb,CAAC;IACN,CAAC;IAED,gBAAgB,CAAC,IAAU;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC;YACrD,OAAO,IAAI,CAAC;QAEhB,MAAM,IAAI,GAAG,aAAa,EAAE;aACvB,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;QAEhE,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE;YAC9B,eAAe,EAAE;gBACb,MAAM,EAAE,YAAY,CAAC,MAAM;gBAC3B,MAAM,EAAE,IAAI;aACf;SACJ,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;YAC7C,OAAO,IAAI,CAAC;QAEhB,OAAO;YACH,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,GAAG,CAAC,UAAU;SACxB,CAAC;IACN,CAAC;IAED,YAAY,CAAC,IAAgB;QACzB,OAAO,IAAI,CAAC,iCAAiC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvF,CAAC;IAED,UAAU,CAAC,IAAgB;QACvB,OAAO,IAAI,CAAC,iCAAiC,CAAC,IAAI,CAAC,IAAI,gCAAgC,CAAC,IAAI,CAAC,CAAC;IAClG,CAAC;IAED,gBAAgB,CAAC,KAA8B;QAC3C,MAAM,cAAc,GAAG,sBAAsB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAEpE,IAAI,CAAC,cAAc,IAAI,CAAC,oBAAoB,CAAC,cAAc,CAAC;YACxD,OAAO,IAAI,CAAC;QAEhB,MAAM,WAAW,GAAG,cAAc,CAAC,WAAW,CAAC;QAC/C,MAAM,YAAY,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAElG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,yBAAyB,CAAC;YAC9C,OAAO,IAAI,CAAC;QAEhB,MAAM,iBAAiB,GAAI,YAA0C,CAAC,GAAG,CAAC,CAAC,CAA0B,EAAE,EAAE;YACrG,MAAM,KAAK,GAAG,sBAAsB,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,OAAO,GAAG,sBAAsB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;YAErD,IAAI,CAAC,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC;gBACpF,OAAO,IAAI,CAAC;YAEhB,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAEtD,IAAI,CAAC,UAAU;gBACX,OAAO,IAAI,CAAC;YAEhB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAE5D,IAAI,YAAY,IAAI,IAAI;gBACpB,OAAO,IAAI,CAAC;YAEhB,OAAO;gBACH,KAAK,EAAE,UAAU;gBACjB,OAAO,EAAE,YAAY;aACxB,CAAC;QACN,CAAC,CAAC;aACG,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;QAE9B,OAAO,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC;IACnE,CAAC;IAED,UAAU,CAAC,KAA8B;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAEjD,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI;YACrB,OAAO,IAAI,CAAC;QAEhB,OAAO;YACH,GAAG,IAAI;YACP,WAAW;SACd,CAAC;IACN,CAAC;IAGD;;OAEG;IACI,gBAAgB;QACnB,OAAO,CAAC,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;IACrC,CAAC;IAED;;OAEG;IAEI,QAAQ;QACX,OAAO,IAAI,CAAC,WAAW,EAAE;aACpB,GAAG,CAAkB,CAAC,CAAC,EAAE,EAAE;YACxB,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC;YAEtB,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;gBAC3B,OAAO,KAAK,CAAC;YAEjB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAClC,OAAO,IAAI,CAAC,iCAAiC,CAAC,CAAC,CAAC;uBAC3C,gCAAgC,CAAC,CAAC,CAAC;uBACnC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;YAEH,MAAM,KAAK,GAAG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAE9C,OAAO;gBACH,KAAK;gBACL,GAAG,EAAE;oBACD,IAAI,EAAE,UAAU;oBAChB,IAAI,EAAE;wBACF,IAAI,EAAE,CAAC,CAAC,OAAO,EAA8B;wBAC7C,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC;qBACtC;iBACJ;aACJ,CAAC;QACN,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IAEK,WAAW;QACf,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE;YAC3D,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU;gBACvB,OAAO,EAAE,CAAC;YAEd,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;YAE5C,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;gBAC5B,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,CAAC;QACb,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAA6C,EAAE,CAAC;QAE3D,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;YAEpC,IAAI,CAAC,IAAI;gBACL,SAAS;YACb,KAAK,MAAM,EAAE,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC;gBAC9B,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,MAAM,CAAC;oBAClC,SAAS;gBACb,KAAK,CAAC,IAAI,CAAC,QAAkD,CAAC,CAAC;YACnE,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,oBAAoB,CAAC,CAAa;QACtC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAExD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;YAC1C,OAAO;QACX,IAAI,CAAC,CAAC,MAAO,GAAG,iBAAiB,CAAC,MAAM,CAAC;YACrC,OAAO;QAEX,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACtC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;gBACtB,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,CAAC;QACb,CAAC,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YACnB,OAAO;QAEX,MAAM,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC;QAE7B,OAAO;YACH,OAAO,EAAE,eAAe,CAAC,WAAW,CAAC;YACrC,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC;YACzC,GAAG,aAAa,CAAC,WAAW,CAAC;YAC7B,MAAM,EAAE,eAAe,CAAC,WAAW,CAAC;SACvC,CAAC;IACN,CAAC;IAEO,WAAW;QACf,OAAO,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;YACpD,MAAM,GAAG,GAAG,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;YAEzC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjC,CAAC,CAAC,CAAC,CAAC;IACR,CAAC;CACJ;AA/WG;IADC,WAAW,EAAE;gDAGb;AAWO;IADP,KAAK,EAAE;yDAyBP;AAoBM;IADN,KAAK,EAAE;mDA2BP;AAyLM;IADN,KAAK,EAAE;iDA6BP;AAMO;IADP,KAAK,EAAE;oDA2BP"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Range } from "@plexcord-companion/shared/Range";
|
|
2
|
+
export type AnyFindType = `find${"Component" | "ByProps" | "Store" | "ByCode" | "ModuleId" | "ComponentByCode" | ""}${"Lazy" | ""}`;
|
|
3
|
+
export type StringNode = {
|
|
4
|
+
type: "string";
|
|
5
|
+
value: string;
|
|
6
|
+
};
|
|
7
|
+
export type RegexNode = {
|
|
8
|
+
type: "regex";
|
|
9
|
+
value: {
|
|
10
|
+
pattern: string;
|
|
11
|
+
flags: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export type FunctionNode = {
|
|
15
|
+
type: "function";
|
|
16
|
+
value: string;
|
|
17
|
+
};
|
|
18
|
+
export type FindNode = StringNode | RegexNode | FunctionNode;
|
|
19
|
+
export type FindData = {
|
|
20
|
+
type: AnyFindType;
|
|
21
|
+
args: FindNode[];
|
|
22
|
+
};
|
|
23
|
+
export type IReplacement = {
|
|
24
|
+
match: StringNode | RegexNode;
|
|
25
|
+
replace: StringNode | FunctionNode;
|
|
26
|
+
};
|
|
27
|
+
export type IFindType = {
|
|
28
|
+
findType: "string";
|
|
29
|
+
/**
|
|
30
|
+
* the find string
|
|
31
|
+
*/
|
|
32
|
+
find: string;
|
|
33
|
+
} | {
|
|
34
|
+
findType: "regex";
|
|
35
|
+
/**
|
|
36
|
+
* stringified regex
|
|
37
|
+
*/
|
|
38
|
+
find: string;
|
|
39
|
+
};
|
|
40
|
+
export type PatchData = IFindType & {
|
|
41
|
+
replacement: IReplacement[];
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* a parsed patch, as it appears in a source file
|
|
45
|
+
*/
|
|
46
|
+
export type SourcePatch = PatchData & {
|
|
47
|
+
range: Range;
|
|
48
|
+
origIndex: number;
|
|
49
|
+
};
|
|
50
|
+
export type FindUse = {
|
|
51
|
+
range: Range;
|
|
52
|
+
use: TestFind;
|
|
53
|
+
};
|
|
54
|
+
export type TestFind = {
|
|
55
|
+
type: "testFind";
|
|
56
|
+
data: FindData;
|
|
57
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC"}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Node } from "typescript";
|
|
2
|
+
import type { FunctionNode, RegexNode, StringNode } from "./types.js";
|
|
3
|
+
export declare function tryParseStringLiteral(node: Node): StringNode | null;
|
|
4
|
+
export declare function tryParseRegularExpressionLiteral(node: Node): RegexNode | null;
|
|
5
|
+
export declare function tryParseFunction(path: string, node: Node): FunctionNode | null;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createPrinter, EmitHint, findConfigFile, isArrowFunction, isFunctionExpression, isRegularExpressionLiteral, isStringLiteral, parseJsonConfigFileContent, readConfigFile, sys, transpileModule, } from "typescript";
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
export function tryParseStringLiteral(node) {
|
|
4
|
+
if (!isStringLiteral(node))
|
|
5
|
+
return null;
|
|
6
|
+
return {
|
|
7
|
+
type: "string",
|
|
8
|
+
value: node.text,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function tryParseRegularExpressionLiteral(node) {
|
|
12
|
+
if (!isRegularExpressionLiteral(node))
|
|
13
|
+
return null;
|
|
14
|
+
const m = node.text.match(/^\/(.+)\/(.*?)$/);
|
|
15
|
+
return m && {
|
|
16
|
+
type: "regex",
|
|
17
|
+
value: {
|
|
18
|
+
pattern: m[1],
|
|
19
|
+
flags: m[2],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function tryParseFunction(path, node) {
|
|
24
|
+
if (!isArrowFunction(node) && !isFunctionExpression(node))
|
|
25
|
+
return null;
|
|
26
|
+
const code = createPrinter()
|
|
27
|
+
.printNode(EmitHint.Expression, node, node.getSourceFile());
|
|
28
|
+
let compilerOptions = {};
|
|
29
|
+
const tsConfigPath = findConfigFile(path, sys.fileExists);
|
|
30
|
+
if (tsConfigPath) {
|
|
31
|
+
const configFile = readConfigFile(tsConfigPath, sys.readFile);
|
|
32
|
+
compilerOptions = parseJsonConfigFileContent(configFile.config, sys, basename(tsConfigPath)).options;
|
|
33
|
+
}
|
|
34
|
+
const res = transpileModule(code, { compilerOptions });
|
|
35
|
+
if (res.diagnostics && res.diagnostics.length > 0)
|
|
36
|
+
return null;
|
|
37
|
+
return {
|
|
38
|
+
type: "function",
|
|
39
|
+
value: res.outputText,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,aAAa,EACb,QAAQ,EACR,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,eAAe,EAEf,0BAA0B,EAC1B,cAAc,EACd,GAAG,EACH,eAAe,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAKrC,MAAM,UAAU,qBAAqB,CAAC,IAAU;IAC5C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;QACtB,OAAO,IAAI,CAAC;IAEhB,OAAO;QACH,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,IAAI,CAAC,IAAI;KACnB,CAAC;AACN,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,IAAU;IACvD,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC;QACjC,OAAO,IAAI,CAAC;IAEhB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAE7C,OAAO,CAAC,IAAI;QACR,IAAI,EAAE,OAAO;QACb,KAAK,EAAE;YACH,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;YACb,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;SACd;KACJ,CAAC;AACN,CAAC;AACD,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,IAAU;IACrD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC;QACrD,OAAO,IAAI,CAAC;IAEhB,MAAM,IAAI,GAAG,aAAa,EAAE;SACvB,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC;IAEhE,IAAI,eAAe,GAAoB,EAAE,CAAC;IAC1C,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IAE1D,IAAI,YAAY,EAAE,CAAC;QACf,MAAM,UAAU,GAAG,cAAc,CAAC,YAAY,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE9D,eAAe,GAAG,0BAA0B,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC;IACzG,CAAC;IAED,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC;IAEvD,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;QAC7C,OAAO,IAAI,CAAC;IAEhB,OAAO;QACH,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,GAAG,CAAC,UAAU;KACxB,CAAC;AACN,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@plexcord-companion/plexcord-ast-parser",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "MutanPlex",
|
|
7
|
+
"url": "https://mutanplex.com"
|
|
8
|
+
},
|
|
9
|
+
"main": "index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
"./*": "./dist/*.js",
|
|
12
|
+
".": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [],
|
|
15
|
+
"license": "LGPL-3.0-or-later",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@plexcord-companion/ast-parser": "2.0.0",
|
|
18
|
+
"@plexcord-companion/shared": "2.0.0"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"ts-api-utils": "^2.1.0",
|
|
22
|
+
"typescript": "^5.9.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^24.6.0",
|
|
26
|
+
"vitest": "^3.2.4"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"preversion": "mkdir .git || :",
|
|
30
|
+
"bump": "npm version",
|
|
31
|
+
"postversion": "rmdir .git",
|
|
32
|
+
"build": "tsc -b && tsc-alias -f"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { DeclarationDomain } from "ts-api-utils";
|
|
2
|
+
import {
|
|
3
|
+
type CallExpression,
|
|
4
|
+
createPrinter,
|
|
5
|
+
EmitHint,
|
|
6
|
+
type Expression,
|
|
7
|
+
type Identifier,
|
|
8
|
+
isArrayLiteralExpression,
|
|
9
|
+
isArrowFunction,
|
|
10
|
+
isCallExpression,
|
|
11
|
+
isFunctionExpression,
|
|
12
|
+
isIdentifier,
|
|
13
|
+
isNamespaceImport,
|
|
14
|
+
isObjectLiteralExpression,
|
|
15
|
+
isPropertyAssignment,
|
|
16
|
+
isRegularExpressionLiteral,
|
|
17
|
+
isStringLiteral,
|
|
18
|
+
isStringLiteralLike,
|
|
19
|
+
isTemplateExpression,
|
|
20
|
+
isVariableDeclaration,
|
|
21
|
+
type Node,
|
|
22
|
+
type ObjectLiteralExpression,
|
|
23
|
+
ScriptTarget,
|
|
24
|
+
transpileModule,
|
|
25
|
+
} from "typescript";
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
AstParser,
|
|
29
|
+
findObjectLiteralByKey,
|
|
30
|
+
findParent,
|
|
31
|
+
getImportName,
|
|
32
|
+
getImportSource,
|
|
33
|
+
type Import,
|
|
34
|
+
isDefaultImport,
|
|
35
|
+
isInImportStatment,
|
|
36
|
+
type WithParent,
|
|
37
|
+
} from "@plexcord-companion/ast-parser";
|
|
38
|
+
import { Cache, CacheGetter } from "@plexcord-companion/shared/decorators";
|
|
39
|
+
import { type Logger, NoopLogger } from "@plexcord-companion/shared/Logger";
|
|
40
|
+
|
|
41
|
+
import type { FindUse, FunctionNode, IFindType, IReplacement, PatchData, RegexNode, SourcePatch, StringNode, TestFind } from "./types";
|
|
42
|
+
import { tryParseRegularExpressionLiteral } from "./util";
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
let logger: Logger = NoopLogger;
|
|
46
|
+
|
|
47
|
+
export function setLogger(newLogger: Logger): void {
|
|
48
|
+
logger = newLogger;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class PlexcordAstParser extends AstParser {
|
|
52
|
+
private readonly _path: string;
|
|
53
|
+
|
|
54
|
+
public get path(): string {
|
|
55
|
+
return this._path;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @CacheGetter
|
|
60
|
+
*/
|
|
61
|
+
@CacheGetter()
|
|
62
|
+
public get imports(): Map<Identifier, Import> {
|
|
63
|
+
return this.listImports();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
constructor(content: string, path: string) {
|
|
67
|
+
super(content);
|
|
68
|
+
this._path = path;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @Cache
|
|
73
|
+
*/
|
|
74
|
+
@Cache()
|
|
75
|
+
private findDefinePlugin(): ObjectLiteralExpression | undefined {
|
|
76
|
+
const define = [...this.imports.values()].find((x) => {
|
|
77
|
+
if (!x.default)
|
|
78
|
+
return;
|
|
79
|
+
if (x.source !== "@utils/types")
|
|
80
|
+
return;
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!define)
|
|
85
|
+
return;
|
|
86
|
+
|
|
87
|
+
const uses = this.vars.get(define.as)?.uses;
|
|
88
|
+
|
|
89
|
+
if (!uses)
|
|
90
|
+
return;
|
|
91
|
+
|
|
92
|
+
const definePlugin = uses.find(({ location }) => {
|
|
93
|
+
if (!isCallExpression(location.parent))
|
|
94
|
+
return;
|
|
95
|
+
return location.parent.arguments.length === 1 && isObjectLiteralExpression(location.parent.arguments[0]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return (definePlugin?.location.parent as CallExpression).arguments[0] as ObjectLiteralExpression;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// TODO: work on files in the plugin folder but not the root plugin file
|
|
102
|
+
public getPluginName(): string | null {
|
|
103
|
+
const definePlugin = this.findDefinePlugin();
|
|
104
|
+
|
|
105
|
+
if (!definePlugin)
|
|
106
|
+
return null;
|
|
107
|
+
|
|
108
|
+
const nameProp = findObjectLiteralByKey(definePlugin, "name");
|
|
109
|
+
|
|
110
|
+
if (!nameProp || !isPropertyAssignment(nameProp) || !isStringLiteral(nameProp.initializer))
|
|
111
|
+
return null;
|
|
112
|
+
return nameProp.initializer.text;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @Cache
|
|
117
|
+
*/
|
|
118
|
+
@Cache()
|
|
119
|
+
public getPatches(): SourcePatch[] {
|
|
120
|
+
const definePlugin = this.findDefinePlugin();
|
|
121
|
+
|
|
122
|
+
if (!definePlugin)
|
|
123
|
+
return [];
|
|
124
|
+
|
|
125
|
+
const patchesProp = findObjectLiteralByKey(definePlugin, "patches");
|
|
126
|
+
|
|
127
|
+
if (!patchesProp || !isPropertyAssignment(patchesProp) || !isArrayLiteralExpression(patchesProp.initializer))
|
|
128
|
+
return [];
|
|
129
|
+
return patchesProp.initializer.elements
|
|
130
|
+
.map((x, origIndex) => {
|
|
131
|
+
if (!isObjectLiteralExpression(x))
|
|
132
|
+
return null;
|
|
133
|
+
|
|
134
|
+
const res = this.parsePatch(x);
|
|
135
|
+
|
|
136
|
+
if (!res)
|
|
137
|
+
return null;
|
|
138
|
+
return {
|
|
139
|
+
...res,
|
|
140
|
+
range: this.makeRangeFromAstNode(x.getChildAt(1)),
|
|
141
|
+
origIndex,
|
|
142
|
+
};
|
|
143
|
+
})
|
|
144
|
+
.filter((x) => x !== null);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
parseFind(patch: ObjectLiteralExpression): IFindType | null {
|
|
148
|
+
const find = findObjectLiteralByKey(patch, "find");
|
|
149
|
+
|
|
150
|
+
if (!find || !isPropertyAssignment(find))
|
|
151
|
+
return null;
|
|
152
|
+
if (!(isStringLiteral(find.initializer) || isRegularExpressionLiteral(find.initializer)))
|
|
153
|
+
return null;
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
findType: isStringLiteral(find.initializer) ? "string" : "regex",
|
|
157
|
+
find: find.initializer.text,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Try to parse a string literal
|
|
163
|
+
*
|
|
164
|
+
* if it is a template literal, attempt to extract the string content by inlining variables
|
|
165
|
+
*/
|
|
166
|
+
tryParseStringLiteral(node: Node): string | null {
|
|
167
|
+
tryParse: if (isStringLiteralLike(node)) {
|
|
168
|
+
return node.text;
|
|
169
|
+
// resolve template literals if they are constant
|
|
170
|
+
} else if (isTemplateExpression(node)) {
|
|
171
|
+
const resolvedSpans = [] as string[];
|
|
172
|
+
|
|
173
|
+
for (const span of node.templateSpans) {
|
|
174
|
+
const spanExpr = span.expression;
|
|
175
|
+
|
|
176
|
+
if (!isIdentifier(spanExpr)) {
|
|
177
|
+
logger.debug(`[PlexcordAstParser] Trying to parse template literal with non-identifier span: ${span.getText()}, FileName: ${span.getSourceFile().fileName}`);
|
|
178
|
+
break tryParse;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const usageInfo = this.getVarInfoFromUse(spanExpr);
|
|
182
|
+
|
|
183
|
+
if (!usageInfo) {
|
|
184
|
+
break tryParse;
|
|
185
|
+
} else if (usageInfo.declarations.length === 0) {
|
|
186
|
+
logger.trace(`[PlexcordAstParser] Could not resolve identifier ${spanExpr.text} to a variable declaration. Is it a global?`);
|
|
187
|
+
break tryParse;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const isConst = this.isConstDeclared(usageInfo);
|
|
191
|
+
|
|
192
|
+
if (!isConst) {
|
|
193
|
+
break tryParse;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const decl = findParent(isConst[0], isVariableDeclaration);
|
|
197
|
+
|
|
198
|
+
if (!decl) {
|
|
199
|
+
break tryParse;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const init = decl.initializer;
|
|
203
|
+
|
|
204
|
+
if (!init) {
|
|
205
|
+
break tryParse;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const initValue = this.tryParseStringLiteral(init);
|
|
209
|
+
|
|
210
|
+
// explicitly check for null to avoid empty string
|
|
211
|
+
if (initValue == null) {
|
|
212
|
+
break tryParse;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
resolvedSpans.push(initValue + span.literal.text);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return node.head.text + resolvedSpans.join("");
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
tryParseStringLiteralToStringNode(node: Node): StringNode | null {
|
|
224
|
+
const str = this.tryParseStringLiteral(node);
|
|
225
|
+
|
|
226
|
+
if (str == null)
|
|
227
|
+
return null;
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
type: "string",
|
|
231
|
+
value: str,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
tryParseFunction(node: Node): FunctionNode | null {
|
|
236
|
+
if (!isArrowFunction(node) && !isFunctionExpression(node))
|
|
237
|
+
return null;
|
|
238
|
+
|
|
239
|
+
const code = createPrinter()
|
|
240
|
+
.printNode(EmitHint.Expression, node, node.getSourceFile());
|
|
241
|
+
|
|
242
|
+
const res = transpileModule(code, {
|
|
243
|
+
compilerOptions: {
|
|
244
|
+
target: ScriptTarget.ESNext,
|
|
245
|
+
strict: true,
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
if (res.diagnostics && res.diagnostics.length > 0)
|
|
250
|
+
return null;
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
type: "function",
|
|
254
|
+
value: res.outputText,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
parseReplace(node: Expression): StringNode | FunctionNode | null {
|
|
259
|
+
return this.tryParseStringLiteralToStringNode(node) ?? this.tryParseFunction(node);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
parseMatch(node: Expression): StringNode | RegexNode | null {
|
|
263
|
+
return this.tryParseStringLiteralToStringNode(node) ?? tryParseRegularExpressionLiteral(node);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
parseReplacement(patch: ObjectLiteralExpression): IReplacement[] | null {
|
|
267
|
+
const replacementObj = findObjectLiteralByKey(patch, "replacement");
|
|
268
|
+
|
|
269
|
+
if (!replacementObj || !isPropertyAssignment(replacementObj))
|
|
270
|
+
return null;
|
|
271
|
+
|
|
272
|
+
const replacement = replacementObj.initializer;
|
|
273
|
+
const replacements = isArrayLiteralExpression(replacement) ? replacement.elements : [replacement];
|
|
274
|
+
|
|
275
|
+
if (!replacements.every(isObjectLiteralExpression))
|
|
276
|
+
return null;
|
|
277
|
+
|
|
278
|
+
const replacementValues = (replacements as ObjectLiteralExpression[]).map((r: ObjectLiteralExpression) => {
|
|
279
|
+
const match = findObjectLiteralByKey(r, "match");
|
|
280
|
+
const replace = findObjectLiteralByKey(r, "replace");
|
|
281
|
+
|
|
282
|
+
if (!replace || !isPropertyAssignment(replace) || !match || !isPropertyAssignment(match))
|
|
283
|
+
return null;
|
|
284
|
+
|
|
285
|
+
const matchValue = this.parseMatch(match.initializer);
|
|
286
|
+
|
|
287
|
+
if (!matchValue)
|
|
288
|
+
return null;
|
|
289
|
+
|
|
290
|
+
const replaceValue = this.parseReplace(replace.initializer);
|
|
291
|
+
|
|
292
|
+
if (replaceValue == null)
|
|
293
|
+
return null;
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
match: matchValue,
|
|
297
|
+
replace: replaceValue,
|
|
298
|
+
};
|
|
299
|
+
})
|
|
300
|
+
.filter((x) => x != null);
|
|
301
|
+
|
|
302
|
+
return replacementValues.length > 0 ? replacementValues : null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
parsePatch(patch: ObjectLiteralExpression): PatchData | null {
|
|
306
|
+
const find = this.parseFind(patch);
|
|
307
|
+
const replacement = this.parseReplacement(patch);
|
|
308
|
+
|
|
309
|
+
if (!replacement || !find)
|
|
310
|
+
return null;
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
...find,
|
|
314
|
+
replacement,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* @returns true if this file is the entry point (if it has a definePlugin call)
|
|
321
|
+
*/
|
|
322
|
+
public isRootPluginFile(): boolean {
|
|
323
|
+
return !!this.findDefinePlugin();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @Cache
|
|
328
|
+
*/
|
|
329
|
+
@Cache()
|
|
330
|
+
public getFinds(): FindUse[] {
|
|
331
|
+
return this.getFindUses()
|
|
332
|
+
.map<FindUse | false>((x) => {
|
|
333
|
+
const call = x.parent;
|
|
334
|
+
|
|
335
|
+
if (call.arguments.length === 0)
|
|
336
|
+
return false;
|
|
337
|
+
|
|
338
|
+
const args = call.arguments.map((x) => {
|
|
339
|
+
return this.tryParseStringLiteralToStringNode(x)
|
|
340
|
+
?? tryParseRegularExpressionLiteral(x)
|
|
341
|
+
?? this.tryParseFunction(x);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const range = this.makeRangeFromAstNode(call);
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
range,
|
|
348
|
+
use: {
|
|
349
|
+
type: "testFind",
|
|
350
|
+
data: {
|
|
351
|
+
type: x.getText() as TestFind["data"]["type"],
|
|
352
|
+
args: args.filter((x) => x != null),
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
})
|
|
357
|
+
.filter((x) => x !== false);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @Cache
|
|
362
|
+
*/
|
|
363
|
+
@Cache()
|
|
364
|
+
private getFindUses(): WithParent<Identifier, CallExpression>[] {
|
|
365
|
+
const imports = [...this.imports.entries()].flatMap(([k, v]) => {
|
|
366
|
+
if (v.source !== "@webpack")
|
|
367
|
+
return [];
|
|
368
|
+
|
|
369
|
+
const origName = (v.orig ?? v.as).getText();
|
|
370
|
+
|
|
371
|
+
if (!origName.startsWith("find"))
|
|
372
|
+
return [];
|
|
373
|
+
return k;
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const toRet: WithParent<Identifier, CallExpression>[] = [];
|
|
377
|
+
|
|
378
|
+
for (const i of imports) {
|
|
379
|
+
const uses = this.vars.get(i)?.uses;
|
|
380
|
+
|
|
381
|
+
if (!uses)
|
|
382
|
+
continue;
|
|
383
|
+
for (const { location } of uses) {
|
|
384
|
+
if (!isCallExpression(location.parent))
|
|
385
|
+
continue;
|
|
386
|
+
toRet.push(location as WithParent<Identifier, CallExpression>);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return toRet;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* returns the import if this identifier is imported
|
|
394
|
+
*/
|
|
395
|
+
private isIdentifierImported(i: Identifier): Import | undefined {
|
|
396
|
+
const { declarations, domain } = this.vars.get(i) ?? {};
|
|
397
|
+
|
|
398
|
+
if (!declarations || declarations.length === 0)
|
|
399
|
+
return;
|
|
400
|
+
if (!(domain! & DeclarationDomain.Import))
|
|
401
|
+
return;
|
|
402
|
+
|
|
403
|
+
const source = declarations.flatMap((x) => {
|
|
404
|
+
if (!isInImportStatment(x))
|
|
405
|
+
return [];
|
|
406
|
+
return x;
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
if (source.length !== 1)
|
|
410
|
+
return;
|
|
411
|
+
|
|
412
|
+
const [importIdent] = source;
|
|
413
|
+
|
|
414
|
+
return {
|
|
415
|
+
default: isDefaultImport(importIdent),
|
|
416
|
+
namespace: isNamespaceImport(importIdent),
|
|
417
|
+
...getImportName(importIdent),
|
|
418
|
+
source: getImportSource(importIdent),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private listImports(): Map<Identifier, Import> {
|
|
423
|
+
return new Map([...this.vars.entries()].flatMap(([k]) => {
|
|
424
|
+
const ret = this.isIdentifierImported(k);
|
|
425
|
+
|
|
426
|
+
return ret ? [[k, ret]] : [];
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Range } from "@plexcord-companion/shared/Range";
|
|
2
|
+
export type AnyFindType
|
|
3
|
+
= `find${"Component" | "ByProps" | "Store" | "ByCode" | "ModuleId" | "ComponentByCode" | ""}${"Lazy" | ""}`;
|
|
4
|
+
|
|
5
|
+
export type StringNode = {
|
|
6
|
+
type: "string";
|
|
7
|
+
value: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type RegexNode = {
|
|
11
|
+
type: "regex";
|
|
12
|
+
value: {
|
|
13
|
+
pattern: string;
|
|
14
|
+
flags: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type FunctionNode = {
|
|
19
|
+
type: "function";
|
|
20
|
+
value: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type FindNode = StringNode | RegexNode | FunctionNode;
|
|
24
|
+
|
|
25
|
+
export type FindData = {
|
|
26
|
+
type: AnyFindType;
|
|
27
|
+
args: FindNode[];
|
|
28
|
+
};
|
|
29
|
+
export type IReplacement = {
|
|
30
|
+
match: StringNode | RegexNode;
|
|
31
|
+
replace: StringNode | FunctionNode;
|
|
32
|
+
};
|
|
33
|
+
export type IFindType
|
|
34
|
+
= | {
|
|
35
|
+
findType: "string";
|
|
36
|
+
/**
|
|
37
|
+
* the find string
|
|
38
|
+
*/
|
|
39
|
+
find: string;
|
|
40
|
+
}
|
|
41
|
+
| {
|
|
42
|
+
findType: "regex";
|
|
43
|
+
/**
|
|
44
|
+
* stringified regex
|
|
45
|
+
*/
|
|
46
|
+
find: string;
|
|
47
|
+
};
|
|
48
|
+
export type PatchData = IFindType & {
|
|
49
|
+
replacement: IReplacement[];
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* a parsed patch, as it appears in a source file
|
|
53
|
+
*/
|
|
54
|
+
export type SourcePatch = PatchData & { range: Range;
|
|
55
|
+
origIndex: number; };
|
|
56
|
+
export type FindUse = {
|
|
57
|
+
range: Range;
|
|
58
|
+
use: TestFind;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type TestFind = {
|
|
62
|
+
type: "testFind";
|
|
63
|
+
data: FindData;
|
|
64
|
+
};
|
package/src/util.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CompilerOptions,
|
|
3
|
+
createPrinter,
|
|
4
|
+
EmitHint,
|
|
5
|
+
findConfigFile,
|
|
6
|
+
isArrowFunction,
|
|
7
|
+
isFunctionExpression,
|
|
8
|
+
isRegularExpressionLiteral,
|
|
9
|
+
isStringLiteral,
|
|
10
|
+
type Node,
|
|
11
|
+
parseJsonConfigFileContent,
|
|
12
|
+
readConfigFile,
|
|
13
|
+
sys,
|
|
14
|
+
transpileModule,
|
|
15
|
+
} from "typescript";
|
|
16
|
+
|
|
17
|
+
import { basename } from "node:path";
|
|
18
|
+
|
|
19
|
+
import type { FunctionNode, RegexNode, StringNode } from "./types";
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export function tryParseStringLiteral(node: Node): StringNode | null {
|
|
23
|
+
if (!isStringLiteral(node))
|
|
24
|
+
return null;
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
type: "string",
|
|
28
|
+
value: node.text,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function tryParseRegularExpressionLiteral(node: Node): RegexNode | null {
|
|
33
|
+
if (!isRegularExpressionLiteral(node))
|
|
34
|
+
return null;
|
|
35
|
+
|
|
36
|
+
const m = node.text.match(/^\/(.+)\/(.*?)$/);
|
|
37
|
+
|
|
38
|
+
return m && {
|
|
39
|
+
type: "regex",
|
|
40
|
+
value: {
|
|
41
|
+
pattern: m[1],
|
|
42
|
+
flags: m[2],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function tryParseFunction(path: string, node: Node): FunctionNode | null {
|
|
47
|
+
if (!isArrowFunction(node) && !isFunctionExpression(node))
|
|
48
|
+
return null;
|
|
49
|
+
|
|
50
|
+
const code = createPrinter()
|
|
51
|
+
.printNode(EmitHint.Expression, node, node.getSourceFile());
|
|
52
|
+
|
|
53
|
+
let compilerOptions: CompilerOptions = {};
|
|
54
|
+
const tsConfigPath = findConfigFile(path, sys.fileExists);
|
|
55
|
+
|
|
56
|
+
if (tsConfigPath) {
|
|
57
|
+
const configFile = readConfigFile(tsConfigPath, sys.readFile);
|
|
58
|
+
|
|
59
|
+
compilerOptions = parseJsonConfigFileContent(configFile.config, sys, basename(tsConfigPath)).options;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const res = transpileModule(code, { compilerOptions });
|
|
63
|
+
|
|
64
|
+
if (res.diagnostics && res.diagnostics.length > 0)
|
|
65
|
+
return null;
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
type: "function",
|
|
69
|
+
value: res.outputText,
|
|
70
|
+
};
|
|
71
|
+
}
|