@plexcord-companion/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/AstParser.d.ts +131 -0
- package/dist/AstParser.js +370 -0
- package/dist/AstParser.js.map +1 -0
- package/dist/StringifiedModule.d.ts +3 -0
- package/dist/StringifiedModule.js +2 -0
- package/dist/StringifiedModule.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +144 -0
- package/dist/util.js +404 -0
- package/dist/util.js.map +1 -0
- package/package.json +33 -0
- package/src/AstParser.ts +450 -0
- package/src/StringifiedModule.ts +3 -0
- package/src/index.ts +3 -0
- package/src/types.ts +31 -0
- package/src/util.ts +571 -0
package/src/util.ts
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
import { isStaticKeyword } from "ts-api-utils";
|
|
2
|
+
import {
|
|
3
|
+
type AssignmentExpression,
|
|
4
|
+
type AssignmentOperatorToken,
|
|
5
|
+
type BinaryExpression,
|
|
6
|
+
type Block,
|
|
7
|
+
type DefaultKeyword,
|
|
8
|
+
forEachChild,
|
|
9
|
+
type Identifier,
|
|
10
|
+
type ImportClause,
|
|
11
|
+
isArrowFunction,
|
|
12
|
+
isBigIntLiteral,
|
|
13
|
+
isBinaryExpression,
|
|
14
|
+
isBlock,
|
|
15
|
+
isConstructorDeclaration,
|
|
16
|
+
isExpressionStatement,
|
|
17
|
+
isFunctionDeclaration,
|
|
18
|
+
isFunctionExpression,
|
|
19
|
+
isGetAccessorDeclaration,
|
|
20
|
+
isIdentifier,
|
|
21
|
+
isImportClause,
|
|
22
|
+
isImportDeclaration,
|
|
23
|
+
isImportSpecifier,
|
|
24
|
+
isJsxText,
|
|
25
|
+
isMethodDeclaration,
|
|
26
|
+
isNamespaceImport as _TS_isNamespaceImport,
|
|
27
|
+
isNumericLiteral,
|
|
28
|
+
isObjectLiteralExpression,
|
|
29
|
+
isPropertyAccessExpression,
|
|
30
|
+
isRegularExpressionLiteral,
|
|
31
|
+
isReturnStatement,
|
|
32
|
+
isSetAccessorDeclaration,
|
|
33
|
+
isStringLiteral,
|
|
34
|
+
isStringLiteralLike,
|
|
35
|
+
isTokenKind,
|
|
36
|
+
isVariableDeclaration,
|
|
37
|
+
type LiteralToken,
|
|
38
|
+
type NamespaceImport,
|
|
39
|
+
type Node,
|
|
40
|
+
type ObjectLiteralElementLike,
|
|
41
|
+
type ObjectLiteralExpression,
|
|
42
|
+
type PlusToken,
|
|
43
|
+
type PropertyAccessExpression,
|
|
44
|
+
type PropertyDeclaration,
|
|
45
|
+
type SourceFile,
|
|
46
|
+
SyntaxKind,
|
|
47
|
+
type SyntaxList,
|
|
48
|
+
type Token,
|
|
49
|
+
type VariableDeclaration,
|
|
50
|
+
} from "typescript";
|
|
51
|
+
|
|
52
|
+
import type { AnyFunction, AssertedType, CBAssertion, Functionish, Import, WithParent } from "./types";
|
|
53
|
+
|
|
54
|
+
export const enum CharCode {
|
|
55
|
+
/**
|
|
56
|
+
* The `\n` character.
|
|
57
|
+
*/
|
|
58
|
+
LineFeed = 10,
|
|
59
|
+
/**
|
|
60
|
+
* The `\r` character.
|
|
61
|
+
*/
|
|
62
|
+
CarriageReturn = 13,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isEOL(char: number): boolean {
|
|
66
|
+
return char === CharCode.CarriageReturn || char === CharCode.LineFeed;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* given a function like this, returns the identifier for x
|
|
71
|
+
* @example function(){
|
|
72
|
+
* // any code here
|
|
73
|
+
* return x;
|
|
74
|
+
* }
|
|
75
|
+
* @param func a function to get the return value of
|
|
76
|
+
* @returns the return identifier, if any
|
|
77
|
+
*/
|
|
78
|
+
export function findReturnIdentifier(func: Functionish): Identifier | undefined {
|
|
79
|
+
if (!func.body)
|
|
80
|
+
return undefined;
|
|
81
|
+
if (isBlock(func.body))
|
|
82
|
+
return _findReturnIdentifier(func.body);
|
|
83
|
+
if (isIdentifier(func.body))
|
|
84
|
+
return func.body;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function _findReturnIdentifier(func: Block): Identifier | undefined {
|
|
88
|
+
const lastStatement = func.statements.at(-1);
|
|
89
|
+
|
|
90
|
+
if (
|
|
91
|
+
!lastStatement
|
|
92
|
+
|| !isReturnStatement(lastStatement)
|
|
93
|
+
|| !lastStatement.expression
|
|
94
|
+
|| !isIdentifier(lastStatement.expression)
|
|
95
|
+
)
|
|
96
|
+
return undefined;
|
|
97
|
+
|
|
98
|
+
return lastStatement.expression;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* given an object literal, returns the property assignment for `prop` if it exists
|
|
103
|
+
*
|
|
104
|
+
* if prop is defined more than once, returns the first
|
|
105
|
+
* @example
|
|
106
|
+
* {
|
|
107
|
+
* exProp: "examplePropValue"
|
|
108
|
+
* }
|
|
109
|
+
* @param prop exProp
|
|
110
|
+
*/
|
|
111
|
+
export function findObjectLiteralByKey(
|
|
112
|
+
object: ObjectLiteralExpression,
|
|
113
|
+
prop: string,
|
|
114
|
+
): ObjectLiteralElementLike | undefined {
|
|
115
|
+
return object.properties.find((x) => x.name?.getText() === prop);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* first parent
|
|
120
|
+
*/
|
|
121
|
+
export const findParent: CBAssertion<undefined, undefined> = (node, func) => {
|
|
122
|
+
if (!node)
|
|
123
|
+
return undefined;
|
|
124
|
+
while (!func(node)) {
|
|
125
|
+
if (!node.parent)
|
|
126
|
+
return undefined;
|
|
127
|
+
node = node.parent;
|
|
128
|
+
}
|
|
129
|
+
return node;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export function findParentLimited<
|
|
133
|
+
F extends (n: Node) => n is Node,
|
|
134
|
+
R extends Node = AssertedType<F, Node>,
|
|
135
|
+
>(
|
|
136
|
+
node: Node,
|
|
137
|
+
func: F extends (n: Node) => n is R ? F : never,
|
|
138
|
+
limit: number,
|
|
139
|
+
): R | undefined {
|
|
140
|
+
if (!node)
|
|
141
|
+
return undefined;
|
|
142
|
+
limit += 1;
|
|
143
|
+
while (limit-- && !func(node)) {
|
|
144
|
+
if (!node.parent)
|
|
145
|
+
return undefined;
|
|
146
|
+
node = node.parent;
|
|
147
|
+
}
|
|
148
|
+
if (limit < 0) {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
return node as R;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// FIXME: try simplifying this
|
|
155
|
+
/**
|
|
156
|
+
* @param node the node to start from
|
|
157
|
+
* @param func a function to check if the parent matches
|
|
158
|
+
*/
|
|
159
|
+
export const lastParent: CBAssertion<undefined, undefined> = (node, func) => {
|
|
160
|
+
if (!node)
|
|
161
|
+
return undefined;
|
|
162
|
+
if (!node.parent)
|
|
163
|
+
return undefined;
|
|
164
|
+
while (func(node.parent)) {
|
|
165
|
+
if (!node.parent)
|
|
166
|
+
break;
|
|
167
|
+
node = node.parent;
|
|
168
|
+
}
|
|
169
|
+
return func(node) ? node : undefined;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export const lastChild: CBAssertion<undefined> = (node, func) => {
|
|
173
|
+
if (!node)
|
|
174
|
+
return undefined;
|
|
175
|
+
|
|
176
|
+
const c = node.getChildren();
|
|
177
|
+
|
|
178
|
+
if (c.length === 0) {
|
|
179
|
+
if (func(node))
|
|
180
|
+
return node;
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
if (c.length === 1) {
|
|
184
|
+
if (func(c[0]))
|
|
185
|
+
return lastChild(c[0], func);
|
|
186
|
+
if (func(node))
|
|
187
|
+
return node;
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const x = one(c, func);
|
|
192
|
+
|
|
193
|
+
if (x) {
|
|
194
|
+
return lastChild(x, func);
|
|
195
|
+
}
|
|
196
|
+
if (func(node))
|
|
197
|
+
return node;
|
|
198
|
+
return undefined;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// FIXME: this seems really stupid
|
|
202
|
+
export function one<
|
|
203
|
+
T,
|
|
204
|
+
F extends (t: T) => t is T,
|
|
205
|
+
R extends T = AssertedType<F, T>,
|
|
206
|
+
>(
|
|
207
|
+
arr: readonly T[],
|
|
208
|
+
func: F extends (t: T) => t is R ? F : never,
|
|
209
|
+
): R | undefined {
|
|
210
|
+
const filter = arr.filter<R>(func);
|
|
211
|
+
|
|
212
|
+
return filter.length === 1 ? filter[0] : undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function isDefaultImport(x: Identifier): x is WithParent<typeof x, ImportClause> {
|
|
216
|
+
return isImportClause(x.parent);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @param node any identifier in an import statment
|
|
221
|
+
*/
|
|
222
|
+
export function getImportName(node: Identifier): Pick<Import, "orig" | "as"> {
|
|
223
|
+
// default or namespace
|
|
224
|
+
if (isDefaultImport(node) || isNamespaceImport(node))
|
|
225
|
+
return { as: node };
|
|
226
|
+
|
|
227
|
+
const specifier = findParent(node, isImportSpecifier);
|
|
228
|
+
|
|
229
|
+
if (!specifier)
|
|
230
|
+
throw new Error("x is not in an import statment");
|
|
231
|
+
return {
|
|
232
|
+
orig: specifier.propertyName,
|
|
233
|
+
as: specifier.name,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// i fucking hate jsdoc
|
|
238
|
+
/**
|
|
239
|
+
* given an access chain like `one.b.three.d` \@*returns* — `[one?, b?]`
|
|
240
|
+
*
|
|
241
|
+
* if b is returned, one is gaurenteed to be defined
|
|
242
|
+
* @param node any node in the property access chain
|
|
243
|
+
*/
|
|
244
|
+
export function getLeadingIdentifier(node: Node | undefined):
|
|
245
|
+
readonly [Identifier, undefined]
|
|
246
|
+
| readonly [Identifier, Identifier]
|
|
247
|
+
| readonly [undefined, undefined] {
|
|
248
|
+
if (!node)
|
|
249
|
+
return [node, undefined];
|
|
250
|
+
|
|
251
|
+
const { expression: module, name: wpExport } = (() => {
|
|
252
|
+
const lastP = lastParent(node, isPropertyAccessExpression);
|
|
253
|
+
|
|
254
|
+
return lastP && lastChild(lastP, isPropertyAccessExpression);
|
|
255
|
+
})() ?? {};
|
|
256
|
+
|
|
257
|
+
if (!module || !isIdentifier(module))
|
|
258
|
+
return [undefined, undefined];
|
|
259
|
+
return [
|
|
260
|
+
module,
|
|
261
|
+
wpExport ? isIdentifier(wpExport) ? wpExport : undefined : undefined,
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function isInImportStatment(x: Node): boolean {
|
|
266
|
+
return findParent(x, isImportDeclaration) != null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @param x an identifier in the import statment, not just any imported identifier
|
|
271
|
+
* @returns the source of the import statment
|
|
272
|
+
* @example
|
|
273
|
+
* ```
|
|
274
|
+
* import { x } from "source"
|
|
275
|
+
* ```
|
|
276
|
+
* @returns "source"
|
|
277
|
+
*/
|
|
278
|
+
export function getImportSource(x: Identifier): string {
|
|
279
|
+
const clause = findParent(x, isImportDeclaration);
|
|
280
|
+
|
|
281
|
+
if (!clause)
|
|
282
|
+
throw new Error("x is not in an import statment");
|
|
283
|
+
// getText returns with quotes, but the prop text does not have them ????
|
|
284
|
+
return clause.moduleSpecifier.getText()
|
|
285
|
+
.slice(1, -1);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function isNamespaceImport(x: Identifier): x is WithParent<typeof x, NamespaceImport> {
|
|
289
|
+
return _TS_isNamespaceImport(x.parent);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function isDefaultKeyword(n: Node): n is DefaultKeyword {
|
|
293
|
+
return n.kind === SyntaxKind.DefaultKeyword;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
export function isSyntaxList(node: Node): node is SyntaxList {
|
|
298
|
+
return node.kind === SyntaxKind.SyntaxList;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* given a function like
|
|
303
|
+
* ```ts
|
|
304
|
+
* function myFunc() {
|
|
305
|
+
* // any code here
|
|
306
|
+
* return a.b; // can be anything else, eg a.b.c a.b[anything]
|
|
307
|
+
* }
|
|
308
|
+
* ```
|
|
309
|
+
* @returns the returned property access expression, if any
|
|
310
|
+
**/
|
|
311
|
+
export function findReturnPropertyAccessExpression(func: AnyFunction): PropertyAccessExpression | undefined {
|
|
312
|
+
if (isBlock(func.body))
|
|
313
|
+
return _findReturnPropertyAccessExpression(func.body);
|
|
314
|
+
if (isPropertyAccessExpression(func.body))
|
|
315
|
+
return func.body;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function _findReturnPropertyAccessExpression(func: Block): PropertyAccessExpression | undefined {
|
|
319
|
+
const lastStatment = func.statements.at(-1);
|
|
320
|
+
|
|
321
|
+
if (
|
|
322
|
+
!lastStatment
|
|
323
|
+
|| !isReturnStatement(lastStatment)
|
|
324
|
+
|| !lastStatment.expression
|
|
325
|
+
|| !isPropertyAccessExpression(lastStatment.expression)
|
|
326
|
+
)
|
|
327
|
+
return undefined;
|
|
328
|
+
|
|
329
|
+
return lastStatment.expression;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function tryParseStringOrNumberLiteral(node: Node | undefined): string | undefined {
|
|
333
|
+
if (!node)
|
|
334
|
+
return;
|
|
335
|
+
if (isStringLiteralLike(node) || isNumericLiteral(node)) {
|
|
336
|
+
return node.text;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function isLiteralish(node: Node): node is LiteralToken {
|
|
341
|
+
return isStringLiteralLike(node)
|
|
342
|
+
|| isNumericLiteral(node)
|
|
343
|
+
|| isBigIntLiteral(node)
|
|
344
|
+
|| isJsxText(node)
|
|
345
|
+
|| isRegularExpressionLiteral(node);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function isFunctionish(node: Node): node is Functionish {
|
|
349
|
+
return (
|
|
350
|
+
isFunctionDeclaration(node)
|
|
351
|
+
|| isMethodDeclaration(node)
|
|
352
|
+
|| isGetAccessorDeclaration(node)
|
|
353
|
+
|| isSetAccessorDeclaration(node)
|
|
354
|
+
|| isConstructorDeclaration(node)
|
|
355
|
+
|| isFunctionExpression(node)
|
|
356
|
+
|| isArrowFunction(node)
|
|
357
|
+
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const DIRECTIVE_PREFIX = "use ";
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* returns if the node is a directive
|
|
365
|
+
* ```ts
|
|
366
|
+
* "use strict"; // true
|
|
367
|
+
* "use something"; // true
|
|
368
|
+
* "not a directive"; // false
|
|
369
|
+
* 42; // false
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
export function isDirective(node: Node): boolean {
|
|
373
|
+
if (!isExpressionStatement(node)) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const { expression } = node;
|
|
378
|
+
|
|
379
|
+
if (!isStringLiteral(expression)) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const { text } = expression;
|
|
384
|
+
|
|
385
|
+
if (text.length <= DIRECTIVE_PREFIX.length) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return text.startsWith(DIRECTIVE_PREFIX);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const ASSIGNMENT_TOKENS: Partial<Record<SyntaxKind, true>> = {
|
|
393
|
+
[SyntaxKind.EqualsToken]: true,
|
|
394
|
+
[SyntaxKind.PlusEqualsToken]: true,
|
|
395
|
+
[SyntaxKind.MinusEqualsToken]: true,
|
|
396
|
+
[SyntaxKind.AsteriskAsteriskEqualsToken]: true,
|
|
397
|
+
[SyntaxKind.AsteriskEqualsToken]: true,
|
|
398
|
+
[SyntaxKind.SlashEqualsToken]: true,
|
|
399
|
+
[SyntaxKind.PercentEqualsToken]: true,
|
|
400
|
+
[SyntaxKind.AmpersandEqualsToken]: true,
|
|
401
|
+
[SyntaxKind.BarEqualsToken]: true,
|
|
402
|
+
[SyntaxKind.CaretEqualsToken]: true,
|
|
403
|
+
[SyntaxKind.LessThanLessThanEqualsToken]: true,
|
|
404
|
+
[SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken]: true,
|
|
405
|
+
[SyntaxKind.GreaterThanGreaterThanEqualsToken]: true,
|
|
406
|
+
[SyntaxKind.BarBarEqualsToken]: true,
|
|
407
|
+
[SyntaxKind.AmpersandAmpersandEqualsToken]: true,
|
|
408
|
+
[SyntaxKind.QuestionQuestionEqualsToken]: true,
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
export function isAssignmentExpression(node: Node | undefined):
|
|
412
|
+
node is AssignmentExpression<AssignmentOperatorToken> {
|
|
413
|
+
if (!node || !isBinaryExpression(node))
|
|
414
|
+
return false;
|
|
415
|
+
|
|
416
|
+
return ASSIGNMENT_TOKENS[node.operatorToken.kind] === true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function isVariableAssignmentLike(node: Node | undefined):
|
|
420
|
+
node is
|
|
421
|
+
| (
|
|
422
|
+
& Omit<VariableDeclaration, "name" | "initializer">
|
|
423
|
+
& {
|
|
424
|
+
name: Identifier;
|
|
425
|
+
initializer: Exclude<VariableDeclaration["initializer"], undefined>;
|
|
426
|
+
}
|
|
427
|
+
)
|
|
428
|
+
| (Omit<AssignmentExpression<AssignmentOperatorToken>, "left"> & { left: Identifier; }) {
|
|
429
|
+
if (!node)
|
|
430
|
+
return false;
|
|
431
|
+
|
|
432
|
+
if (isVariableDeclaration(node)) {
|
|
433
|
+
return isIdentifier(node.name) && !!node.initializer;
|
|
434
|
+
} else if (isBinaryExpression(node)) {
|
|
435
|
+
return isAssignmentExpression(node);
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export function isBinaryPlusExpression(node: Node):
|
|
441
|
+
node is
|
|
442
|
+
& BinaryExpression
|
|
443
|
+
& {
|
|
444
|
+
readonly operatorToken: PlusToken;
|
|
445
|
+
} {
|
|
446
|
+
if (!isBinaryExpression(node)) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
if (node.operatorToken.kind !== SyntaxKind.PlusToken) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* @license MIT
|
|
458
|
+
* taken from tsutils, license below
|
|
459
|
+
* The MIT License (MIT)
|
|
460
|
+
*
|
|
461
|
+
* Copyright (c) 2017 Klaus Meinhardt
|
|
462
|
+
*
|
|
463
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
464
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
465
|
+
* in the Software without restriction, including without limitation the rights
|
|
466
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
467
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
468
|
+
* furnished to do so, subject to the following conditions:
|
|
469
|
+
*
|
|
470
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
471
|
+
* copies or substantial portions of the Software.
|
|
472
|
+
*
|
|
473
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
474
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
475
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
476
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
477
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
478
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
479
|
+
* SOFTWARE.
|
|
480
|
+
*/
|
|
481
|
+
|
|
482
|
+
// empty comment so the license doesn't become the doc comment for this func
|
|
483
|
+
|
|
484
|
+
/***/
|
|
485
|
+
export function getTokenAtPosition(
|
|
486
|
+
parent: Node,
|
|
487
|
+
pos: number,
|
|
488
|
+
sourceFile?: SourceFile,
|
|
489
|
+
allowJsDoc?: boolean,
|
|
490
|
+
): Node | undefined {
|
|
491
|
+
if (pos < parent.pos || pos >= parent.end) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (isTokenKind(parent.kind)) {
|
|
495
|
+
return parent;
|
|
496
|
+
}
|
|
497
|
+
return _getTokenAtPosition(parent, pos, sourceFile ?? parent.getSourceFile(), allowJsDoc === true);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function _getTokenAtPosition(node: Node, pos: number, sourceFile: SourceFile, allowJsDoc: boolean): Node | undefined {
|
|
501
|
+
if (!allowJsDoc) {
|
|
502
|
+
// if we are not interested in JSDoc, we can skip to the deepest AST node at the given position
|
|
503
|
+
node = getAstNodeAtPosition(node, pos)!;
|
|
504
|
+
if (isTokenKind(node.kind)) {
|
|
505
|
+
return node;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
outer: while (true) {
|
|
509
|
+
for (const child of node.getChildren()) {
|
|
510
|
+
if (child.end > pos && (allowJsDoc || child.kind !== SyntaxKind.JSDoc)) {
|
|
511
|
+
if (isTokenKind(child.kind)) {
|
|
512
|
+
return child;
|
|
513
|
+
}
|
|
514
|
+
node = child;
|
|
515
|
+
continue outer;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** Returns the deepest AST Node at `pos`. Returns undefined if `pos` is outside of the range of `node` */
|
|
523
|
+
export function getAstNodeAtPosition(node: Node, pos: number): Node | undefined {
|
|
524
|
+
if (node.pos > pos || node.end <= pos) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
while (isNodeKind(node.kind)) {
|
|
528
|
+
const nested = forEachChild(node, (child) => (child.pos <= pos && child.end > pos ? child : undefined));
|
|
529
|
+
|
|
530
|
+
if (nested === undefined) {
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
node = nested;
|
|
534
|
+
}
|
|
535
|
+
return node;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* stolen form tsutils, seems sketchy
|
|
540
|
+
*/
|
|
541
|
+
function isNodeKind(kind: SyntaxKind) {
|
|
542
|
+
return kind >= SyntaxKind.FirstNode;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export function nonNull<T>(x: T | null | undefined): x is T {
|
|
546
|
+
return x != null;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
export function isEmptyObjectLiteral(node: Node): node is ObjectLiteralExpression {
|
|
550
|
+
return isObjectLiteralExpression(node) && node.properties.length === 0;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export function isCommaExpression(node: Node): node is
|
|
554
|
+
& BinaryExpression
|
|
555
|
+
& {
|
|
556
|
+
readonly operatorToken: Token<SyntaxKind.CommaToken>;
|
|
557
|
+
} {
|
|
558
|
+
return isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.CommaToken;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export function isInExpression(node: Node): node is
|
|
562
|
+
& BinaryExpression
|
|
563
|
+
& {
|
|
564
|
+
readonly operatorToken: Token<SyntaxKind.InKeyword>;
|
|
565
|
+
} {
|
|
566
|
+
return isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.InKeyword;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export function isStatic(item: PropertyDeclaration): boolean {
|
|
570
|
+
return item.modifiers?.some((mod) => isStaticKeyword(mod)) ?? false;
|
|
571
|
+
}
|