@cjser/jsdoc-type-pratt-parser 9.1.1-cjser.2
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/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/index.d.mts +733 -0
- package/dist/index.mjs +3091 -0
- package/dist-cjser/index.cjs +3027 -0
- package/package.json +159 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,3091 @@
|
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
function tokenToString(token) {
|
|
3
|
+
if (token.text !== void 0 && token.text !== "") return `'${token.type}' with value '${token.text}'`;
|
|
4
|
+
else return `'${token.type}'`;
|
|
5
|
+
}
|
|
6
|
+
var NoParsletFoundError = class NoParsletFoundError extends Error {
|
|
7
|
+
token;
|
|
8
|
+
constructor(token) {
|
|
9
|
+
super(`No parslet found for token: ${tokenToString(token)}`);
|
|
10
|
+
this.token = token;
|
|
11
|
+
Object.setPrototypeOf(this, NoParsletFoundError.prototype);
|
|
12
|
+
}
|
|
13
|
+
getToken() {
|
|
14
|
+
return this.token;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var EarlyEndOfParseError = class EarlyEndOfParseError extends Error {
|
|
18
|
+
token;
|
|
19
|
+
constructor(token) {
|
|
20
|
+
super(`The parsing ended early. The next token was: ${tokenToString(token)}`);
|
|
21
|
+
this.token = token;
|
|
22
|
+
Object.setPrototypeOf(this, EarlyEndOfParseError.prototype);
|
|
23
|
+
}
|
|
24
|
+
getToken() {
|
|
25
|
+
return this.token;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var UnexpectedTypeError = class UnexpectedTypeError extends Error {
|
|
29
|
+
constructor(result, message) {
|
|
30
|
+
let error = `Unexpected type: '${result.type}'.`;
|
|
31
|
+
if (message !== void 0) error += ` Message: ${message}`;
|
|
32
|
+
super(error);
|
|
33
|
+
Object.setPrototypeOf(this, UnexpectedTypeError.prototype);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/lexer/Token.ts
|
|
38
|
+
const baseNameTokens = [
|
|
39
|
+
"module",
|
|
40
|
+
"keyof",
|
|
41
|
+
"event",
|
|
42
|
+
"external",
|
|
43
|
+
"readonly",
|
|
44
|
+
"is",
|
|
45
|
+
"typeof",
|
|
46
|
+
"in",
|
|
47
|
+
"null",
|
|
48
|
+
"undefined",
|
|
49
|
+
"function",
|
|
50
|
+
"asserts",
|
|
51
|
+
"infer",
|
|
52
|
+
"extends",
|
|
53
|
+
"import"
|
|
54
|
+
];
|
|
55
|
+
const reservedWordsAsRootTSTypes = [
|
|
56
|
+
"false",
|
|
57
|
+
"null",
|
|
58
|
+
"true",
|
|
59
|
+
"void"
|
|
60
|
+
];
|
|
61
|
+
[...reservedWordsAsRootTSTypes];
|
|
62
|
+
const reservedWords$1 = {
|
|
63
|
+
always: [
|
|
64
|
+
"break",
|
|
65
|
+
"case",
|
|
66
|
+
"catch",
|
|
67
|
+
"class",
|
|
68
|
+
"const",
|
|
69
|
+
"continue",
|
|
70
|
+
"debugger",
|
|
71
|
+
"default",
|
|
72
|
+
"delete",
|
|
73
|
+
"do",
|
|
74
|
+
"else",
|
|
75
|
+
"export",
|
|
76
|
+
"extends",
|
|
77
|
+
"false",
|
|
78
|
+
"finally",
|
|
79
|
+
"for",
|
|
80
|
+
"function",
|
|
81
|
+
"if",
|
|
82
|
+
"import",
|
|
83
|
+
"in",
|
|
84
|
+
"instanceof",
|
|
85
|
+
"new",
|
|
86
|
+
"null",
|
|
87
|
+
"return",
|
|
88
|
+
"super",
|
|
89
|
+
"switch",
|
|
90
|
+
"this",
|
|
91
|
+
"throw",
|
|
92
|
+
"true",
|
|
93
|
+
"try",
|
|
94
|
+
"typeof",
|
|
95
|
+
"var",
|
|
96
|
+
"void",
|
|
97
|
+
"while",
|
|
98
|
+
"with"
|
|
99
|
+
],
|
|
100
|
+
strictMode: [
|
|
101
|
+
"let",
|
|
102
|
+
"static",
|
|
103
|
+
"yield"
|
|
104
|
+
],
|
|
105
|
+
moduleOrAsyncFunctionBodies: ["await"]
|
|
106
|
+
};
|
|
107
|
+
const futureReservedWords = {
|
|
108
|
+
always: ["enum"],
|
|
109
|
+
strictMode: [
|
|
110
|
+
"implements",
|
|
111
|
+
"interface",
|
|
112
|
+
"package",
|
|
113
|
+
"private",
|
|
114
|
+
"protected",
|
|
115
|
+
"public"
|
|
116
|
+
]
|
|
117
|
+
};
|
|
118
|
+
const strictModeNonIdentifiers = ["arguments", "eval"];
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/assertTypes.ts
|
|
121
|
+
function assertResultIsNotReservedWord(parser, result) {
|
|
122
|
+
let text;
|
|
123
|
+
if (result.type === "JsdocTypeName") text = result.value;
|
|
124
|
+
else if (result.type === "JsdocTypeParenthesis") {
|
|
125
|
+
let res = result;
|
|
126
|
+
while (res.type === "JsdocTypeParenthesis") res = res.element;
|
|
127
|
+
if (res.type === "JsdocTypeName") text = res.value;
|
|
128
|
+
else return result;
|
|
129
|
+
} else return result;
|
|
130
|
+
if (reservedWords$1.always.includes(text) && !reservedWordsAsRootTSTypes.includes(text) && (text !== "this" || parser.classContext !== true)) throw new Error(`Unexpected reserved keyword "${text}"`);
|
|
131
|
+
if (futureReservedWords.always.includes(text)) throw new Error(`Unexpected future reserved keyword "${text}"`);
|
|
132
|
+
if (parser.module !== void 0 && parser.module || parser.strictMode !== void 0 && parser.strictMode) {
|
|
133
|
+
if (reservedWords$1.strictMode.includes(text)) throw new Error(`Unexpected reserved keyword "${text}" for strict mode`);
|
|
134
|
+
if (futureReservedWords.strictMode.includes(text)) throw new Error(`Unexpected future reserved keyword "${text}" for strict mode`);
|
|
135
|
+
if (strictModeNonIdentifiers.includes(text)) throw new Error(`The item "${text}" is not an identifier in strict mode`);
|
|
136
|
+
}
|
|
137
|
+
if (parser.module !== void 0 && parser.module || parser.asyncFunctionBody !== void 0 && parser.asyncFunctionBody) {
|
|
138
|
+
if (reservedWords$1.moduleOrAsyncFunctionBodies.includes(text)) throw new Error(`Unexpected reserved keyword "${text}" for modules or async function bodies`);
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Throws an error if the provided result is not a {@link RootResult}
|
|
144
|
+
*/
|
|
145
|
+
function assertRootResult(result) {
|
|
146
|
+
if (result === void 0) throw new Error("Unexpected undefined");
|
|
147
|
+
if (result.type === "JsdocTypeKeyValue" || result.type === "JsdocTypeParameterList" || result.type === "JsdocTypeProperty" || result.type === "JsdocTypeReadonlyProperty" || result.type === "JsdocTypeObjectField" || result.type === "JsdocTypeJsdocObjectField" || result.type === "JsdocTypeIndexSignature" || result.type === "JsdocTypeMappedType" || result.type === "JsdocTypeTypeParameter" || result.type === "JsdocTypeCallSignature" || result.type === "JsdocTypeConstructorSignature" || result.type === "JsdocTypeMethodSignature" || result.type === "JsdocTypeIndexedAccessIndex" || result.type === "JsdocTypeComputedProperty" || result.type === "JsdocTypeComputedMethod") throw new UnexpectedTypeError(result);
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
function assertPlainKeyValueOrRootResult(result) {
|
|
151
|
+
if (result.type === "JsdocTypeKeyValue") return assertPlainKeyValueResult(result);
|
|
152
|
+
return assertRootResult(result);
|
|
153
|
+
}
|
|
154
|
+
function assertPlainKeyValueOrNameResult(result) {
|
|
155
|
+
if (result.type === "JsdocTypeName") return result;
|
|
156
|
+
return assertPlainKeyValueResult(result);
|
|
157
|
+
}
|
|
158
|
+
function assertPlainKeyValueResult(result) {
|
|
159
|
+
if (result.type !== "JsdocTypeKeyValue") throw new UnexpectedTypeError(result);
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
function assertNumberOrVariadicNameResult(result) {
|
|
163
|
+
if (result.type === "JsdocTypeVariadic") {
|
|
164
|
+
if (result.element?.type === "JsdocTypeName") return result;
|
|
165
|
+
throw new UnexpectedTypeError(result);
|
|
166
|
+
}
|
|
167
|
+
if (result.type !== "JsdocTypeNumber" && result.type !== "JsdocTypeName") throw new UnexpectedTypeError(result);
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
function assertArrayOrTupleResult(result) {
|
|
171
|
+
if (result.type === "JsdocTypeTuple") return result;
|
|
172
|
+
if (result.type === "JsdocTypeGeneric" && result.meta.brackets === "square") return result;
|
|
173
|
+
throw new UnexpectedTypeError(result);
|
|
174
|
+
}
|
|
175
|
+
function isSquaredProperty(result) {
|
|
176
|
+
return result.type === "JsdocTypeIndexSignature" || result.type === "JsdocTypeMappedType";
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/Parser.ts
|
|
180
|
+
var Parser = class {
|
|
181
|
+
grammar;
|
|
182
|
+
_lexer;
|
|
183
|
+
baseParser;
|
|
184
|
+
externalParsers;
|
|
185
|
+
module;
|
|
186
|
+
strictMode;
|
|
187
|
+
asyncFunctionBody;
|
|
188
|
+
classContext;
|
|
189
|
+
rangeStart;
|
|
190
|
+
range;
|
|
191
|
+
locStart;
|
|
192
|
+
loc;
|
|
193
|
+
constructor(grammar, lexer, baseParser, { module, strictMode, asyncFunctionBody, classContext, range = false, rangeStart = 0, loc = false, locStart = {
|
|
194
|
+
line: 1,
|
|
195
|
+
column: 0
|
|
196
|
+
}, externalParsers } = {}) {
|
|
197
|
+
this.grammar = grammar;
|
|
198
|
+
this._lexer = lexer;
|
|
199
|
+
this.baseParser = baseParser;
|
|
200
|
+
this.externalParsers = externalParsers;
|
|
201
|
+
this.module = module;
|
|
202
|
+
this.strictMode = strictMode;
|
|
203
|
+
this.asyncFunctionBody = asyncFunctionBody;
|
|
204
|
+
this.classContext = classContext;
|
|
205
|
+
this.rangeStart = rangeStart;
|
|
206
|
+
this.range = range;
|
|
207
|
+
this.locStart = locStart;
|
|
208
|
+
this.loc = loc;
|
|
209
|
+
}
|
|
210
|
+
get lexer() {
|
|
211
|
+
return this._lexer;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Parses a given string and throws an error if the parse ended before the end of the string.
|
|
215
|
+
*/
|
|
216
|
+
parse() {
|
|
217
|
+
const result = this.parseType(0);
|
|
218
|
+
if (this.lexer.current.type !== "EOF") throw new EarlyEndOfParseError(this.lexer.current);
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Parses with the current lexer and asserts that the result is a {@link RootResult}.
|
|
223
|
+
*/
|
|
224
|
+
parseType(precedence) {
|
|
225
|
+
return assertRootResult(this.parseIntermediateType(precedence));
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The main parsing function. First it tries to parse the current state in the prefix step, and then it continues
|
|
229
|
+
* to parse the state in the infix step.
|
|
230
|
+
*/
|
|
231
|
+
parseIntermediateType(precedence) {
|
|
232
|
+
const result = this.tryParslets(null, precedence);
|
|
233
|
+
if (result === null) throw new NoParsletFoundError(this.lexer.current);
|
|
234
|
+
return this.parseInfixIntermediateType(result, precedence);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* In the infix parsing step the parser continues to parse the current state with all parslets until none returns
|
|
238
|
+
* a result.
|
|
239
|
+
*/
|
|
240
|
+
parseInfixIntermediateType(left, precedence) {
|
|
241
|
+
let result = this.tryParslets(left, precedence);
|
|
242
|
+
while (result !== null) {
|
|
243
|
+
left = result;
|
|
244
|
+
result = this.tryParslets(left, precedence);
|
|
245
|
+
}
|
|
246
|
+
return left;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Tries to parse the current state with all parslets in the grammar and returns the first non null result.
|
|
250
|
+
*/
|
|
251
|
+
tryParslets(left, precedence) {
|
|
252
|
+
for (const parslet of this.grammar) {
|
|
253
|
+
const rangeStart = this.rangeStart;
|
|
254
|
+
const locStartLine = this.locStart.line;
|
|
255
|
+
const locStartColumn = this.locStart.column;
|
|
256
|
+
const result = parslet(this, precedence, left);
|
|
257
|
+
if (result !== null) {
|
|
258
|
+
if (this.range) result.range = [rangeStart, this.rangeStart];
|
|
259
|
+
if (this.loc) result.loc = {
|
|
260
|
+
end: {
|
|
261
|
+
line: this.locStart.line,
|
|
262
|
+
column: this.locStart.column
|
|
263
|
+
},
|
|
264
|
+
start: {
|
|
265
|
+
line: locStartLine,
|
|
266
|
+
column: locStartColumn
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* If the given type equals the current type of the {@link Lexer} advance the lexer. Return true if the lexer was
|
|
276
|
+
* advanced.
|
|
277
|
+
*/
|
|
278
|
+
consume(types) {
|
|
279
|
+
if (!Array.isArray(types)) types = [types];
|
|
280
|
+
if (types.includes(this.lexer.current.type)) {
|
|
281
|
+
if (this.range)
|
|
282
|
+
/* c8 ignore next -- Default is for TS */
|
|
283
|
+
this.rangeStart += this.lexer.current?.reduced ?? 0;
|
|
284
|
+
if (this.loc) {
|
|
285
|
+
/* c8 ignore next 4 -- Defaults are for TS */
|
|
286
|
+
this.locStart.line += this.lexer.current?.line ?? 0;
|
|
287
|
+
this.locStart.column = (this.lexer.current?.line ?? 0) > 0 ? this.lexer.current?.column ?? 0 : this.locStart.column + (this.lexer.current?.column ?? 0);
|
|
288
|
+
}
|
|
289
|
+
this._lexer = this.lexer.advance();
|
|
290
|
+
return true;
|
|
291
|
+
} else return false;
|
|
292
|
+
}
|
|
293
|
+
acceptLexerState(parser) {
|
|
294
|
+
this._lexer = parser.lexer;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/parslets/isQuestionMarkUnknownType.ts
|
|
299
|
+
function isQuestionMarkUnknownType(next) {
|
|
300
|
+
return next === "}" || next === "EOF" || next === "|" || next === "," || next === ")" || next === ">";
|
|
301
|
+
}
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region src/parslets/NullableParslets.ts
|
|
304
|
+
const nullableParslet = (parser, precedence, left) => {
|
|
305
|
+
const type = parser.lexer.current.type;
|
|
306
|
+
const next = parser.lexer.next.type;
|
|
307
|
+
const inferSuffix = left?.type === "JsdocTypeInfer";
|
|
308
|
+
if (!(left === null && type === "?" && !isQuestionMarkUnknownType(next) || left !== null && !inferSuffix && type === "?" && 12 > precedence)) return null;
|
|
309
|
+
parser.consume("?");
|
|
310
|
+
if (left === null) return {
|
|
311
|
+
type: "JsdocTypeNullable",
|
|
312
|
+
element: parser.parseType(12),
|
|
313
|
+
meta: { position: "prefix" }
|
|
314
|
+
};
|
|
315
|
+
else return {
|
|
316
|
+
type: "JsdocTypeNullable",
|
|
317
|
+
element: assertRootResult(left),
|
|
318
|
+
meta: { position: "suffix" }
|
|
319
|
+
};
|
|
320
|
+
};
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/parslets/Parslet.ts
|
|
323
|
+
function composeParslet(options) {
|
|
324
|
+
const parslet = (parser, curPrecedence, left) => {
|
|
325
|
+
const type = parser.lexer.current.type;
|
|
326
|
+
const next = parser.lexer.next.type;
|
|
327
|
+
if (left === null) {
|
|
328
|
+
if ("parsePrefix" in options) {
|
|
329
|
+
if (options.accept(type, next)) return options.parsePrefix(parser);
|
|
330
|
+
}
|
|
331
|
+
} else if ("parseInfix" in options) {
|
|
332
|
+
if (options.precedence > curPrecedence && options.accept(type, next)) return options.parseInfix(parser, left);
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
};
|
|
336
|
+
Object.defineProperty(parslet, "name", { value: options.name });
|
|
337
|
+
return parslet;
|
|
338
|
+
}
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region src/parslets/OptionalParslet.ts
|
|
341
|
+
const optionalParslet = composeParslet({
|
|
342
|
+
name: "optionalParslet",
|
|
343
|
+
accept: (type) => type === "=",
|
|
344
|
+
precedence: 11,
|
|
345
|
+
parsePrefix: (parser) => {
|
|
346
|
+
parser.consume("=");
|
|
347
|
+
return {
|
|
348
|
+
type: "JsdocTypeOptional",
|
|
349
|
+
element: parser.parseType(11),
|
|
350
|
+
meta: { position: "prefix" }
|
|
351
|
+
};
|
|
352
|
+
},
|
|
353
|
+
parseInfix: (parser, left) => {
|
|
354
|
+
parser.consume("=");
|
|
355
|
+
return {
|
|
356
|
+
type: "JsdocTypeOptional",
|
|
357
|
+
element: assertRootResult(left),
|
|
358
|
+
meta: { position: "suffix" }
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
//#endregion
|
|
363
|
+
//#region src/parslets/NumberParslet.ts
|
|
364
|
+
const numberParslet = composeParslet({
|
|
365
|
+
name: "numberParslet",
|
|
366
|
+
accept: (type) => type === "Number",
|
|
367
|
+
parsePrefix: (parser) => {
|
|
368
|
+
const text = parser.lexer.current.text;
|
|
369
|
+
parser.consume("Number");
|
|
370
|
+
if (text.endsWith("n")) {
|
|
371
|
+
const bigintText = text.slice(0, -1);
|
|
372
|
+
return {
|
|
373
|
+
type: "JsdocTypeBigInt",
|
|
374
|
+
value: BigInt(bigintText).toString()
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
return {
|
|
378
|
+
type: "JsdocTypeNumber",
|
|
379
|
+
value: parseFloat(text)
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/parslets/ParenthesisParslet.ts
|
|
385
|
+
const parenthesisParslet = composeParslet({
|
|
386
|
+
name: "parenthesisParslet",
|
|
387
|
+
accept: (type) => type === "(",
|
|
388
|
+
parsePrefix: (parser) => {
|
|
389
|
+
parser.consume("(");
|
|
390
|
+
if (parser.consume(")")) return {
|
|
391
|
+
type: "JsdocTypeParameterList",
|
|
392
|
+
elements: []
|
|
393
|
+
};
|
|
394
|
+
const result = parser.parseIntermediateType(0);
|
|
395
|
+
if (!parser.consume(")")) throw new Error("Unterminated parenthesis");
|
|
396
|
+
if (result.type === "JsdocTypeParameterList") return result;
|
|
397
|
+
else if (result.type === "JsdocTypeKeyValue") return {
|
|
398
|
+
type: "JsdocTypeParameterList",
|
|
399
|
+
elements: [result]
|
|
400
|
+
};
|
|
401
|
+
return {
|
|
402
|
+
type: "JsdocTypeParenthesis",
|
|
403
|
+
element: assertRootResult(result)
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
//#endregion
|
|
408
|
+
//#region src/parslets/SpecialTypesParslet.ts
|
|
409
|
+
const specialTypesParslet = composeParslet({
|
|
410
|
+
name: "specialTypesParslet",
|
|
411
|
+
accept: (type, next) => type === "?" && isQuestionMarkUnknownType(next) || type === "null" || type === "undefined" || type === "*",
|
|
412
|
+
parsePrefix: (parser) => {
|
|
413
|
+
if (parser.consume("null")) return { type: "JsdocTypeNull" };
|
|
414
|
+
if (parser.consume("undefined")) return { type: "JsdocTypeUndefined" };
|
|
415
|
+
if (parser.consume("*")) return { type: "JsdocTypeAny" };
|
|
416
|
+
if (parser.consume("?")) return { type: "JsdocTypeUnknown" };
|
|
417
|
+
throw new Error(`Unacceptable token: ${parser.lexer.current.text}`);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/parslets/NotNullableParslet.ts
|
|
422
|
+
const notNullableParslet = composeParslet({
|
|
423
|
+
name: "notNullableParslet",
|
|
424
|
+
accept: (type) => type === "!",
|
|
425
|
+
precedence: 12,
|
|
426
|
+
parsePrefix: (parser) => {
|
|
427
|
+
parser.consume("!");
|
|
428
|
+
return {
|
|
429
|
+
type: "JsdocTypeNotNullable",
|
|
430
|
+
element: parser.parseType(12),
|
|
431
|
+
meta: { position: "prefix" }
|
|
432
|
+
};
|
|
433
|
+
},
|
|
434
|
+
parseInfix: (parser, left) => {
|
|
435
|
+
parser.consume("!");
|
|
436
|
+
return {
|
|
437
|
+
type: "JsdocTypeNotNullable",
|
|
438
|
+
element: assertRootResult(left),
|
|
439
|
+
meta: { position: "suffix" }
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/parslets/ParameterListParslet.ts
|
|
445
|
+
function createParameterListParslet({ allowTrailingComma }) {
|
|
446
|
+
return composeParslet({
|
|
447
|
+
name: "parameterListParslet",
|
|
448
|
+
accept: (type) => type === ",",
|
|
449
|
+
precedence: 1,
|
|
450
|
+
parseInfix: (parser, left) => {
|
|
451
|
+
const elements = [assertPlainKeyValueOrRootResult(left)];
|
|
452
|
+
parser.consume(",");
|
|
453
|
+
do
|
|
454
|
+
try {
|
|
455
|
+
const next = parser.parseIntermediateType(1);
|
|
456
|
+
elements.push(assertPlainKeyValueOrRootResult(next));
|
|
457
|
+
} catch (e) {
|
|
458
|
+
if (allowTrailingComma && e instanceof NoParsletFoundError) break;
|
|
459
|
+
else throw e;
|
|
460
|
+
}
|
|
461
|
+
while (parser.consume(","));
|
|
462
|
+
if (elements.length > 0 && elements.slice(0, -1).some((e) => e.type === "JsdocTypeVariadic")) throw new Error("Only the last parameter may be a rest parameter");
|
|
463
|
+
return {
|
|
464
|
+
type: "JsdocTypeParameterList",
|
|
465
|
+
elements
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
471
|
+
//#region src/parslets/GenericParslet.ts
|
|
472
|
+
const genericParslet = composeParslet({
|
|
473
|
+
name: "genericParslet",
|
|
474
|
+
accept: (type, next) => type === "<" || type === "." && next === "<",
|
|
475
|
+
precedence: 17,
|
|
476
|
+
parseInfix: (parser, left) => {
|
|
477
|
+
const dot = parser.consume(".");
|
|
478
|
+
parser.consume("<");
|
|
479
|
+
const elements = [];
|
|
480
|
+
do
|
|
481
|
+
if (parser.consume("infer")) {
|
|
482
|
+
const name = parser.parseIntermediateType(12);
|
|
483
|
+
if (name.type !== "JsdocTypeName") throw new UnexpectedTypeError(name, "A typescript infer always has to have a name.");
|
|
484
|
+
elements.push({
|
|
485
|
+
type: "JsdocTypeInfer",
|
|
486
|
+
element: name
|
|
487
|
+
});
|
|
488
|
+
} else elements.push(parser.parseType(1));
|
|
489
|
+
while (parser.consume(","));
|
|
490
|
+
if (!parser.consume(">")) throw new Error("Unterminated generic parameter list");
|
|
491
|
+
return {
|
|
492
|
+
type: "JsdocTypeGeneric",
|
|
493
|
+
left: assertRootResult(left),
|
|
494
|
+
elements,
|
|
495
|
+
meta: {
|
|
496
|
+
brackets: "angle",
|
|
497
|
+
dot
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
//#endregion
|
|
503
|
+
//#region src/parslets/UnionParslets.ts
|
|
504
|
+
const unionParslet = composeParslet({
|
|
505
|
+
name: "unionParslet",
|
|
506
|
+
accept: (type) => type === "|",
|
|
507
|
+
precedence: 5,
|
|
508
|
+
parseInfix: (parser, left) => {
|
|
509
|
+
parser.consume("|");
|
|
510
|
+
const elements = [];
|
|
511
|
+
do
|
|
512
|
+
elements.push(parser.parseType(5));
|
|
513
|
+
while (parser.consume("|"));
|
|
514
|
+
return {
|
|
515
|
+
type: "JsdocTypeUnion",
|
|
516
|
+
elements: [assertResultIsNotReservedWord(parser, assertRootResult(left)), ...elements.map((element) => assertResultIsNotReservedWord(parser, element))]
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
//#endregion
|
|
521
|
+
//#region src/grammars/baseGrammar.ts
|
|
522
|
+
const baseGrammar = [
|
|
523
|
+
nullableParslet,
|
|
524
|
+
optionalParslet,
|
|
525
|
+
numberParslet,
|
|
526
|
+
parenthesisParslet,
|
|
527
|
+
specialTypesParslet,
|
|
528
|
+
notNullableParslet,
|
|
529
|
+
createParameterListParslet({ allowTrailingComma: true }),
|
|
530
|
+
genericParslet,
|
|
531
|
+
unionParslet,
|
|
532
|
+
optionalParslet
|
|
533
|
+
];
|
|
534
|
+
//#endregion
|
|
535
|
+
//#region src/parslets/NamePathParslet.ts
|
|
536
|
+
function createNamePathParslet({ allowSquareBracketsOnAnyType, allowJsdocNamePaths, pathGrammar }) {
|
|
537
|
+
return function namePathParslet(parser, precedence, left) {
|
|
538
|
+
if (left === null || precedence >= 18) return null;
|
|
539
|
+
const type = parser.lexer.current.type;
|
|
540
|
+
const next = parser.lexer.next.type;
|
|
541
|
+
if (!(type === "." && next !== "<" || type === "[" && (allowSquareBracketsOnAnyType || left.type === "JsdocTypeName") || allowJsdocNamePaths && (type === "~" || type === "#"))) return null;
|
|
542
|
+
let pathType;
|
|
543
|
+
let brackets = false;
|
|
544
|
+
if (parser.consume(".")) pathType = "property";
|
|
545
|
+
else if (parser.consume("[")) {
|
|
546
|
+
pathType = "property-brackets";
|
|
547
|
+
brackets = true;
|
|
548
|
+
} else if (parser.consume("~")) pathType = "inner";
|
|
549
|
+
else {
|
|
550
|
+
parser.consume("#");
|
|
551
|
+
pathType = "instance";
|
|
552
|
+
}
|
|
553
|
+
const pathParser = brackets && allowSquareBracketsOnAnyType ? parser : pathGrammar !== null ? new Parser(pathGrammar, parser.lexer, parser) : parser;
|
|
554
|
+
const parsed = pathParser.parseType(brackets && allowSquareBracketsOnAnyType ? 0 : 18);
|
|
555
|
+
parser.acceptLexerState(pathParser);
|
|
556
|
+
let right;
|
|
557
|
+
switch (parsed.type) {
|
|
558
|
+
case "JsdocTypeName":
|
|
559
|
+
right = {
|
|
560
|
+
type: "JsdocTypeProperty",
|
|
561
|
+
value: parsed.value,
|
|
562
|
+
meta: { quote: void 0 }
|
|
563
|
+
};
|
|
564
|
+
break;
|
|
565
|
+
case "JsdocTypeNumber":
|
|
566
|
+
right = {
|
|
567
|
+
type: "JsdocTypeProperty",
|
|
568
|
+
value: parsed.value.toString(10),
|
|
569
|
+
meta: { quote: void 0 }
|
|
570
|
+
};
|
|
571
|
+
break;
|
|
572
|
+
case "JsdocTypeBigInt": throw new UnexpectedTypeError(parsed, "Expecting 'JsdocTypeName', 'JsdocTypeNumber', 'JsdocStringValue' or 'JsdocTypeSpecialNamePath'");
|
|
573
|
+
case "JsdocTypeStringValue":
|
|
574
|
+
right = {
|
|
575
|
+
type: "JsdocTypeProperty",
|
|
576
|
+
value: parsed.value,
|
|
577
|
+
meta: { quote: parsed.meta.quote }
|
|
578
|
+
};
|
|
579
|
+
break;
|
|
580
|
+
case "JsdocTypeSpecialNamePath":
|
|
581
|
+
if (parsed.specialType === "event") right = parsed;
|
|
582
|
+
else throw new UnexpectedTypeError(parsed, "Type 'JsdocTypeSpecialNamePath' is only allowed with specialType 'event'");
|
|
583
|
+
break;
|
|
584
|
+
default:
|
|
585
|
+
if (!brackets || !allowSquareBracketsOnAnyType) throw new UnexpectedTypeError(parsed, "Expecting 'JsdocTypeName', 'JsdocTypeNumber', 'JsdocStringValue' or 'JsdocTypeSpecialNamePath'");
|
|
586
|
+
right = {
|
|
587
|
+
type: "JsdocTypeIndexedAccessIndex",
|
|
588
|
+
right: parsed
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
if (brackets && !parser.consume("]")) {
|
|
592
|
+
const token = parser.lexer.current;
|
|
593
|
+
throw new Error(`Unterminated square brackets. Next token is '${token.type}' with text '${token.text}'`);
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
type: "JsdocTypeNamePath",
|
|
597
|
+
left: assertRootResult(left),
|
|
598
|
+
right,
|
|
599
|
+
pathType
|
|
600
|
+
};
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
//#endregion
|
|
604
|
+
//#region src/parslets/NameParslet.ts
|
|
605
|
+
function createNameParslet({ allowedAdditionalTokens }) {
|
|
606
|
+
return composeParslet({
|
|
607
|
+
name: "nameParslet",
|
|
608
|
+
accept: (type) => type === "Identifier" || type === "this" || type === "new" || allowedAdditionalTokens.includes(type),
|
|
609
|
+
parsePrefix: (parser) => {
|
|
610
|
+
const { type, text } = parser.lexer.current;
|
|
611
|
+
parser.consume(type);
|
|
612
|
+
return {
|
|
613
|
+
type: "JsdocTypeName",
|
|
614
|
+
value: text
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
//#endregion
|
|
620
|
+
//#region src/parslets/StringValueParslet.ts
|
|
621
|
+
const stringValueParslet = composeParslet({
|
|
622
|
+
name: "stringValueParslet",
|
|
623
|
+
accept: (type) => type === "StringValue",
|
|
624
|
+
parsePrefix: (parser) => {
|
|
625
|
+
const text = parser.lexer.current.text;
|
|
626
|
+
parser.consume("StringValue");
|
|
627
|
+
return {
|
|
628
|
+
type: "JsdocTypeStringValue",
|
|
629
|
+
value: text.slice(1, -1),
|
|
630
|
+
meta: { quote: text.startsWith("'") ? "single" : "double" }
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
//#endregion
|
|
635
|
+
//#region src/parslets/SpecialNamePathParslet.ts
|
|
636
|
+
function createSpecialNamePathParslet({ pathGrammar, allowedTypes }) {
|
|
637
|
+
return composeParslet({
|
|
638
|
+
name: "specialNamePathParslet",
|
|
639
|
+
accept: (type) => allowedTypes.includes(type),
|
|
640
|
+
parsePrefix: (parser) => {
|
|
641
|
+
const type = parser.lexer.current.type;
|
|
642
|
+
parser.consume(type);
|
|
643
|
+
if (!parser.consume(":")) return {
|
|
644
|
+
type: "JsdocTypeName",
|
|
645
|
+
value: type
|
|
646
|
+
};
|
|
647
|
+
let result;
|
|
648
|
+
let token = parser.lexer.current;
|
|
649
|
+
if (parser.consume("StringValue")) result = {
|
|
650
|
+
type: "JsdocTypeSpecialNamePath",
|
|
651
|
+
value: token.text.slice(1, -1),
|
|
652
|
+
specialType: type,
|
|
653
|
+
meta: { quote: token.text.startsWith("'") ? "single" : "double" }
|
|
654
|
+
};
|
|
655
|
+
else {
|
|
656
|
+
let value = "";
|
|
657
|
+
const allowed = [
|
|
658
|
+
"Identifier",
|
|
659
|
+
"@",
|
|
660
|
+
"/"
|
|
661
|
+
];
|
|
662
|
+
while (allowed.some((type) => parser.consume(type))) {
|
|
663
|
+
value += token.text;
|
|
664
|
+
token = parser.lexer.current;
|
|
665
|
+
}
|
|
666
|
+
result = {
|
|
667
|
+
type: "JsdocTypeSpecialNamePath",
|
|
668
|
+
value,
|
|
669
|
+
specialType: type,
|
|
670
|
+
meta: { quote: void 0 }
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
const moduleParser = new Parser(pathGrammar, parser.lexer, parser);
|
|
674
|
+
const moduleResult = moduleParser.parseInfixIntermediateType(result, 0);
|
|
675
|
+
parser.acceptLexerState(moduleParser);
|
|
676
|
+
return assertRootResult(moduleResult);
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
//#endregion
|
|
681
|
+
//#region src/grammars/pathGrammar.ts
|
|
682
|
+
const basePathGrammar = [
|
|
683
|
+
createNameParslet({ allowedAdditionalTokens: ["external", "module"] }),
|
|
684
|
+
stringValueParslet,
|
|
685
|
+
numberParslet,
|
|
686
|
+
createNamePathParslet({
|
|
687
|
+
allowSquareBracketsOnAnyType: false,
|
|
688
|
+
allowJsdocNamePaths: true,
|
|
689
|
+
pathGrammar: null
|
|
690
|
+
})
|
|
691
|
+
];
|
|
692
|
+
const pathGrammar = [
|
|
693
|
+
...basePathGrammar,
|
|
694
|
+
createSpecialNamePathParslet({
|
|
695
|
+
allowedTypes: ["event"],
|
|
696
|
+
pathGrammar: basePathGrammar
|
|
697
|
+
}),
|
|
698
|
+
createNameParslet({ allowedAdditionalTokens: baseNameTokens })
|
|
699
|
+
];
|
|
700
|
+
//#endregion
|
|
701
|
+
//#region src/parslets/FunctionParslet.ts
|
|
702
|
+
function getParameters(value) {
|
|
703
|
+
let parameters;
|
|
704
|
+
if (value.type === "JsdocTypeParameterList") parameters = value.elements;
|
|
705
|
+
else if (value.type === "JsdocTypeParenthesis") parameters = [value.element];
|
|
706
|
+
else throw new UnexpectedTypeError(value);
|
|
707
|
+
return parameters.map((p) => assertPlainKeyValueOrRootResult(p));
|
|
708
|
+
}
|
|
709
|
+
function getUnnamedParameters(value) {
|
|
710
|
+
const parameters = getParameters(value);
|
|
711
|
+
if (parameters.some((p) => p.type === "JsdocTypeKeyValue")) throw new Error("No parameter should be named");
|
|
712
|
+
return parameters;
|
|
713
|
+
}
|
|
714
|
+
function createFunctionParslet({ allowNamedParameters, allowNoReturnType, allowWithoutParenthesis, allowNewAsFunctionKeyword }) {
|
|
715
|
+
return composeParslet({
|
|
716
|
+
name: "functionParslet",
|
|
717
|
+
accept: (type, next) => type === "function" || allowNewAsFunctionKeyword && type === "new" && next === "(",
|
|
718
|
+
parsePrefix: (parser) => {
|
|
719
|
+
const newKeyword = parser.consume("new");
|
|
720
|
+
parser.consume("function");
|
|
721
|
+
const hasParenthesis = parser.lexer.current.type === "(";
|
|
722
|
+
if (!hasParenthesis) {
|
|
723
|
+
if (!allowWithoutParenthesis) throw new Error("function is missing parameter list");
|
|
724
|
+
return {
|
|
725
|
+
type: "JsdocTypeName",
|
|
726
|
+
value: "function"
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
let result = {
|
|
730
|
+
type: "JsdocTypeFunction",
|
|
731
|
+
parameters: [],
|
|
732
|
+
arrow: false,
|
|
733
|
+
constructor: newKeyword,
|
|
734
|
+
parenthesis: hasParenthesis
|
|
735
|
+
};
|
|
736
|
+
const value = parser.parseIntermediateType(14);
|
|
737
|
+
if (allowNamedParameters === void 0) result.parameters = getUnnamedParameters(value);
|
|
738
|
+
else if (newKeyword && value.type === "JsdocTypeFunction" && value.arrow) {
|
|
739
|
+
result = value;
|
|
740
|
+
result.constructor = true;
|
|
741
|
+
return result;
|
|
742
|
+
} else {
|
|
743
|
+
result.parameters = getParameters(value);
|
|
744
|
+
for (const p of result.parameters) if (p.type === "JsdocTypeKeyValue" && !allowNamedParameters.includes(p.key)) throw new Error(`only allowed named parameters are ${allowNamedParameters.join(", ")} but got ${p.type}`);
|
|
745
|
+
}
|
|
746
|
+
if (parser.consume(":")) result.returnType = parser.parseType(7);
|
|
747
|
+
else if (!allowNoReturnType) throw new Error("function is missing return type");
|
|
748
|
+
return result;
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/parslets/VariadicParslet.ts
|
|
754
|
+
function createVariadicParslet({ allowPostfix, allowEnclosingBrackets }) {
|
|
755
|
+
return composeParslet({
|
|
756
|
+
name: "variadicParslet",
|
|
757
|
+
accept: (type) => type === "...",
|
|
758
|
+
precedence: 7,
|
|
759
|
+
parsePrefix: (parser) => {
|
|
760
|
+
parser.consume("...");
|
|
761
|
+
const brackets = allowEnclosingBrackets && parser.consume("[");
|
|
762
|
+
try {
|
|
763
|
+
const element = parser.parseType(7);
|
|
764
|
+
if (brackets && !parser.consume("]")) throw new Error("Unterminated variadic type. Missing ']'");
|
|
765
|
+
return {
|
|
766
|
+
type: "JsdocTypeVariadic",
|
|
767
|
+
element: assertRootResult(element),
|
|
768
|
+
meta: {
|
|
769
|
+
position: "prefix",
|
|
770
|
+
squareBrackets: brackets
|
|
771
|
+
}
|
|
772
|
+
};
|
|
773
|
+
} catch (e) {
|
|
774
|
+
if (e instanceof NoParsletFoundError) {
|
|
775
|
+
if (brackets) throw new Error("Empty square brackets for variadic are not allowed.", { cause: e });
|
|
776
|
+
return {
|
|
777
|
+
type: "JsdocTypeVariadic",
|
|
778
|
+
meta: {
|
|
779
|
+
position: void 0,
|
|
780
|
+
squareBrackets: false
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
} else throw e;
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
parseInfix: allowPostfix ? (parser, left) => {
|
|
787
|
+
parser.consume("...");
|
|
788
|
+
return {
|
|
789
|
+
type: "JsdocTypeVariadic",
|
|
790
|
+
element: assertRootResult(left),
|
|
791
|
+
meta: {
|
|
792
|
+
position: "suffix",
|
|
793
|
+
squareBrackets: false
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
} : void 0
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
//#endregion
|
|
800
|
+
//#region src/parslets/SymbolParslet.ts
|
|
801
|
+
const symbolParslet = composeParslet({
|
|
802
|
+
name: "symbolParslet",
|
|
803
|
+
accept: (type) => type === "(",
|
|
804
|
+
precedence: 10,
|
|
805
|
+
parseInfix: (parser, left) => {
|
|
806
|
+
if (left.type !== "JsdocTypeName") throw new Error("Symbol expects a name on the left side. (Reacting on '(')");
|
|
807
|
+
parser.consume("(");
|
|
808
|
+
const result = {
|
|
809
|
+
type: "JsdocTypeSymbol",
|
|
810
|
+
value: left.value
|
|
811
|
+
};
|
|
812
|
+
if (!parser.consume(")")) {
|
|
813
|
+
result.element = assertNumberOrVariadicNameResult(parser.parseIntermediateType(10));
|
|
814
|
+
if (!parser.consume(")")) throw new Error("Symbol does not end after value");
|
|
815
|
+
}
|
|
816
|
+
return result;
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
//#endregion
|
|
820
|
+
//#region src/parslets/ArrayBracketsParslet.ts
|
|
821
|
+
const arrayBracketsParslet = composeParslet({
|
|
822
|
+
name: "arrayBracketsParslet",
|
|
823
|
+
precedence: 16,
|
|
824
|
+
accept: (type, next) => type === "[" && next === "]",
|
|
825
|
+
parseInfix: (parser, left) => {
|
|
826
|
+
parser.consume("[");
|
|
827
|
+
parser.consume("]");
|
|
828
|
+
return {
|
|
829
|
+
type: "JsdocTypeGeneric",
|
|
830
|
+
left: {
|
|
831
|
+
type: "JsdocTypeName",
|
|
832
|
+
value: "Array"
|
|
833
|
+
},
|
|
834
|
+
elements: [assertRootResult(left)],
|
|
835
|
+
meta: {
|
|
836
|
+
brackets: "square",
|
|
837
|
+
dot: false
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
//#endregion
|
|
843
|
+
//#region src/parslets/ObjectParslet.ts
|
|
844
|
+
function createObjectParslet({ signatureGrammar, objectFieldGrammar, allowKeyTypes }) {
|
|
845
|
+
return composeParslet({
|
|
846
|
+
name: "objectParslet",
|
|
847
|
+
accept: (type) => type === "{",
|
|
848
|
+
parsePrefix: (parser) => {
|
|
849
|
+
parser.consume("{");
|
|
850
|
+
const result = {
|
|
851
|
+
type: "JsdocTypeObject",
|
|
852
|
+
meta: { separator: "comma" },
|
|
853
|
+
elements: []
|
|
854
|
+
};
|
|
855
|
+
if (!parser.consume("}")) {
|
|
856
|
+
let separator;
|
|
857
|
+
const fieldParser = new Parser(objectFieldGrammar, parser.lexer, parser, parser.externalParsers?.computedPropertyParser !== void 0 ? { externalParsers: { computedPropertyParser: parser.externalParsers.computedPropertyParser } } : void 0);
|
|
858
|
+
while (true) {
|
|
859
|
+
fieldParser.acceptLexerState(parser);
|
|
860
|
+
let field = fieldParser.parseIntermediateType(2);
|
|
861
|
+
parser.acceptLexerState(fieldParser);
|
|
862
|
+
if (field === void 0 && allowKeyTypes) field = parser.parseIntermediateType(2);
|
|
863
|
+
let optional = false;
|
|
864
|
+
if (field.type === "JsdocTypeNullable") {
|
|
865
|
+
optional = true;
|
|
866
|
+
field = field.element;
|
|
867
|
+
}
|
|
868
|
+
if (field.type === "JsdocTypeNumber" || field.type === "JsdocTypeName" || field.type === "JsdocTypeStringValue") {
|
|
869
|
+
let quote;
|
|
870
|
+
if (field.type === "JsdocTypeStringValue") quote = field.meta.quote;
|
|
871
|
+
result.elements.push({
|
|
872
|
+
type: "JsdocTypeObjectField",
|
|
873
|
+
key: field.value.toString(),
|
|
874
|
+
right: void 0,
|
|
875
|
+
optional,
|
|
876
|
+
readonly: false,
|
|
877
|
+
meta: { quote }
|
|
878
|
+
});
|
|
879
|
+
} else if (signatureGrammar !== void 0 && (field.type === "JsdocTypeCallSignature" || field.type === "JsdocTypeConstructorSignature" || field.type === "JsdocTypeMethodSignature")) {
|
|
880
|
+
const signatureParser = new Parser([...signatureGrammar, ...parser.grammar.flatMap((grammar) => {
|
|
881
|
+
if (grammar.name === "keyValueParslet") return [];
|
|
882
|
+
return [grammar];
|
|
883
|
+
})], parser.lexer, parser);
|
|
884
|
+
signatureParser.acceptLexerState(parser);
|
|
885
|
+
const params = signatureParser.parseIntermediateType(2);
|
|
886
|
+
parser.acceptLexerState(signatureParser);
|
|
887
|
+
field.parameters = getParameters(params);
|
|
888
|
+
const returnType = parser.parseType(2);
|
|
889
|
+
field.returnType = returnType;
|
|
890
|
+
result.elements.push(field);
|
|
891
|
+
} else if (field.type === "JsdocTypeObjectField" || field.type === "JsdocTypeJsdocObjectField") result.elements.push(field);
|
|
892
|
+
else if (field.type === "JsdocTypeReadonlyProperty" && field.element.type === "JsdocTypeObjectField") {
|
|
893
|
+
if (typeof field.element.key === "object" && field.element.key.type === "JsdocTypeComputedMethod") throw new Error("Computed method may not be readonly");
|
|
894
|
+
field.element.readonly = true;
|
|
895
|
+
result.elements.push(field.element);
|
|
896
|
+
} else throw new UnexpectedTypeError(field);
|
|
897
|
+
if (parser.lexer.current.startOfLine) {
|
|
898
|
+
separator ??= "linebreak";
|
|
899
|
+
parser.consume(",") || parser.consume(";");
|
|
900
|
+
} else if (parser.consume(",")) if (parser.lexer.current.startOfLine) separator = "comma-and-linebreak";
|
|
901
|
+
else separator = "comma";
|
|
902
|
+
else if (parser.consume(";")) if (parser.lexer.current.startOfLine) separator = "semicolon-and-linebreak";
|
|
903
|
+
else separator = "semicolon";
|
|
904
|
+
else break;
|
|
905
|
+
if (parser.lexer.current.type === "}") break;
|
|
906
|
+
}
|
|
907
|
+
result.meta.separator = separator ?? "comma";
|
|
908
|
+
if ((separator ?? "").endsWith("linebreak")) result.meta.propertyIndent = " ";
|
|
909
|
+
if (!parser.consume("}")) throw new Error("Unterminated record type. Missing '}'");
|
|
910
|
+
}
|
|
911
|
+
return result;
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
//#endregion
|
|
916
|
+
//#region src/parslets/ObjectFieldParslet.ts
|
|
917
|
+
function createObjectFieldParslet({ allowSquaredProperties, allowKeyTypes, allowReadonly, allowOptional }) {
|
|
918
|
+
return composeParslet({
|
|
919
|
+
name: "objectFieldParslet",
|
|
920
|
+
precedence: 3,
|
|
921
|
+
accept: (type) => type === ":",
|
|
922
|
+
parseInfix: (parser, left) => {
|
|
923
|
+
let optional = false;
|
|
924
|
+
let readonlyProperty = false;
|
|
925
|
+
if (allowOptional && left.type === "JsdocTypeNullable") {
|
|
926
|
+
optional = true;
|
|
927
|
+
left = left.element;
|
|
928
|
+
}
|
|
929
|
+
if (allowReadonly && left.type === "JsdocTypeReadonlyProperty") {
|
|
930
|
+
readonlyProperty = true;
|
|
931
|
+
left = left.element;
|
|
932
|
+
}
|
|
933
|
+
if (left.type === "JsdocTypeBigInt") throw new UnexpectedTypeError(left);
|
|
934
|
+
/* c8 ignore next 2 -- Always has base parser? */
|
|
935
|
+
const parentParser = parser.baseParser ?? parser;
|
|
936
|
+
parentParser.acceptLexerState(parser);
|
|
937
|
+
if (left.type === "JsdocTypeNumber" || left.type === "JsdocTypeName" || left.type === "JsdocTypeStringValue" || isSquaredProperty(left)) {
|
|
938
|
+
/* c8 ignore next 3 -- Guard */
|
|
939
|
+
if (isSquaredProperty(left) && !allowSquaredProperties) throw new UnexpectedTypeError(left);
|
|
940
|
+
parentParser.consume(":");
|
|
941
|
+
let quote;
|
|
942
|
+
if (left.type === "JsdocTypeStringValue") quote = left.meta.quote;
|
|
943
|
+
const right = parentParser.parseType(3);
|
|
944
|
+
parser.acceptLexerState(parentParser);
|
|
945
|
+
return {
|
|
946
|
+
type: "JsdocTypeObjectField",
|
|
947
|
+
/* c8 ignore next -- Guard; not needed anymore? */
|
|
948
|
+
key: isSquaredProperty(left) ? left : left.value.toString(),
|
|
949
|
+
right,
|
|
950
|
+
optional,
|
|
951
|
+
readonly: readonlyProperty,
|
|
952
|
+
meta: { quote }
|
|
953
|
+
};
|
|
954
|
+
} else {
|
|
955
|
+
if (!allowKeyTypes) throw new UnexpectedTypeError(left);
|
|
956
|
+
parentParser.consume(":");
|
|
957
|
+
const right = parentParser.parseType(3);
|
|
958
|
+
parser.acceptLexerState(parentParser);
|
|
959
|
+
return {
|
|
960
|
+
type: "JsdocTypeJsdocObjectField",
|
|
961
|
+
left: assertRootResult(left),
|
|
962
|
+
right
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
//#endregion
|
|
969
|
+
//#region src/parslets/KeyValueParslet.ts
|
|
970
|
+
function createKeyValueParslet({ allowOptional, allowVariadic, acceptParameterList }) {
|
|
971
|
+
return composeParslet({
|
|
972
|
+
name: "keyValueParslet",
|
|
973
|
+
precedence: 3,
|
|
974
|
+
accept: (type) => type === ":",
|
|
975
|
+
parseInfix: (parser, left) => {
|
|
976
|
+
let optional = false;
|
|
977
|
+
let variadic = false;
|
|
978
|
+
if (allowOptional && left.type === "JsdocTypeNullable") {
|
|
979
|
+
optional = true;
|
|
980
|
+
left = left.element;
|
|
981
|
+
}
|
|
982
|
+
if (allowVariadic && left.type === "JsdocTypeVariadic" && left.element !== void 0) {
|
|
983
|
+
variadic = true;
|
|
984
|
+
left = left.element;
|
|
985
|
+
}
|
|
986
|
+
if (left.type !== "JsdocTypeName") {
|
|
987
|
+
if (acceptParameterList !== void 0 && left.type === "JsdocTypeParameterList") {
|
|
988
|
+
parser.consume(":");
|
|
989
|
+
return left;
|
|
990
|
+
}
|
|
991
|
+
throw new UnexpectedTypeError(left);
|
|
992
|
+
}
|
|
993
|
+
parser.consume(":");
|
|
994
|
+
const right = parser.parseType(3);
|
|
995
|
+
return {
|
|
996
|
+
type: "JsdocTypeKeyValue",
|
|
997
|
+
key: left.value,
|
|
998
|
+
right,
|
|
999
|
+
optional,
|
|
1000
|
+
variadic
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
//#endregion
|
|
1006
|
+
//#region src/grammars/jsdocGrammar.ts
|
|
1007
|
+
const jsdocBaseGrammar = [
|
|
1008
|
+
...baseGrammar,
|
|
1009
|
+
createFunctionParslet({
|
|
1010
|
+
allowWithoutParenthesis: true,
|
|
1011
|
+
allowNamedParameters: ["this", "new"],
|
|
1012
|
+
allowNoReturnType: true,
|
|
1013
|
+
allowNewAsFunctionKeyword: false
|
|
1014
|
+
}),
|
|
1015
|
+
stringValueParslet,
|
|
1016
|
+
createSpecialNamePathParslet({
|
|
1017
|
+
allowedTypes: [
|
|
1018
|
+
"module",
|
|
1019
|
+
"external",
|
|
1020
|
+
"event"
|
|
1021
|
+
],
|
|
1022
|
+
pathGrammar
|
|
1023
|
+
}),
|
|
1024
|
+
createVariadicParslet({
|
|
1025
|
+
allowEnclosingBrackets: true,
|
|
1026
|
+
allowPostfix: true
|
|
1027
|
+
}),
|
|
1028
|
+
createNameParslet({ allowedAdditionalTokens: ["keyof"] }),
|
|
1029
|
+
symbolParslet,
|
|
1030
|
+
arrayBracketsParslet,
|
|
1031
|
+
createNamePathParslet({
|
|
1032
|
+
allowSquareBracketsOnAnyType: false,
|
|
1033
|
+
allowJsdocNamePaths: true,
|
|
1034
|
+
pathGrammar
|
|
1035
|
+
})
|
|
1036
|
+
];
|
|
1037
|
+
const jsdocGrammar = [
|
|
1038
|
+
...jsdocBaseGrammar,
|
|
1039
|
+
createObjectParslet({
|
|
1040
|
+
objectFieldGrammar: [
|
|
1041
|
+
createNameParslet({ allowedAdditionalTokens: [
|
|
1042
|
+
"typeof",
|
|
1043
|
+
"module",
|
|
1044
|
+
"in"
|
|
1045
|
+
] }),
|
|
1046
|
+
createObjectFieldParslet({
|
|
1047
|
+
allowSquaredProperties: false,
|
|
1048
|
+
allowKeyTypes: true,
|
|
1049
|
+
allowOptional: false,
|
|
1050
|
+
allowReadonly: false
|
|
1051
|
+
}),
|
|
1052
|
+
...jsdocBaseGrammar
|
|
1053
|
+
],
|
|
1054
|
+
allowKeyTypes: true
|
|
1055
|
+
}),
|
|
1056
|
+
createKeyValueParslet({
|
|
1057
|
+
allowOptional: true,
|
|
1058
|
+
allowVariadic: true
|
|
1059
|
+
})
|
|
1060
|
+
];
|
|
1061
|
+
const jsdocNameGrammar = [
|
|
1062
|
+
genericParslet,
|
|
1063
|
+
arrayBracketsParslet,
|
|
1064
|
+
createNameParslet({ allowedAdditionalTokens: baseNameTokens })
|
|
1065
|
+
];
|
|
1066
|
+
const jsdocNamePathGrammar = [
|
|
1067
|
+
genericParslet,
|
|
1068
|
+
arrayBracketsParslet,
|
|
1069
|
+
createNameParslet({ allowedAdditionalTokens: baseNameTokens }),
|
|
1070
|
+
createNamePathParslet({
|
|
1071
|
+
allowSquareBracketsOnAnyType: false,
|
|
1072
|
+
allowJsdocNamePaths: true,
|
|
1073
|
+
pathGrammar
|
|
1074
|
+
})
|
|
1075
|
+
];
|
|
1076
|
+
const jsdocNamePathSpecialGrammar = [createSpecialNamePathParslet({
|
|
1077
|
+
allowedTypes: [
|
|
1078
|
+
"module",
|
|
1079
|
+
"external",
|
|
1080
|
+
"event"
|
|
1081
|
+
],
|
|
1082
|
+
pathGrammar
|
|
1083
|
+
}), ...jsdocNamePathGrammar];
|
|
1084
|
+
//#endregion
|
|
1085
|
+
//#region src/parslets/TypeOfParslet.ts
|
|
1086
|
+
const typeOfParslet = composeParslet({
|
|
1087
|
+
name: "typeOfParslet",
|
|
1088
|
+
accept: (type) => type === "typeof",
|
|
1089
|
+
parsePrefix: (parser) => {
|
|
1090
|
+
parser.consume("typeof");
|
|
1091
|
+
return {
|
|
1092
|
+
type: "JsdocTypeTypeof",
|
|
1093
|
+
element: parser.parseType(13)
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
//#endregion
|
|
1098
|
+
//#region src/grammars/closureGrammar.ts
|
|
1099
|
+
const objectFieldGrammar$1 = [
|
|
1100
|
+
createNameParslet({ allowedAdditionalTokens: [
|
|
1101
|
+
"typeof",
|
|
1102
|
+
"module",
|
|
1103
|
+
"keyof",
|
|
1104
|
+
"event",
|
|
1105
|
+
"external",
|
|
1106
|
+
"in"
|
|
1107
|
+
] }),
|
|
1108
|
+
nullableParslet,
|
|
1109
|
+
optionalParslet,
|
|
1110
|
+
stringValueParslet,
|
|
1111
|
+
numberParslet,
|
|
1112
|
+
createObjectFieldParslet({
|
|
1113
|
+
allowSquaredProperties: false,
|
|
1114
|
+
allowKeyTypes: false,
|
|
1115
|
+
allowOptional: false,
|
|
1116
|
+
allowReadonly: false
|
|
1117
|
+
})
|
|
1118
|
+
];
|
|
1119
|
+
const closureGrammar = [
|
|
1120
|
+
...baseGrammar,
|
|
1121
|
+
createObjectParslet({
|
|
1122
|
+
allowKeyTypes: false,
|
|
1123
|
+
objectFieldGrammar: objectFieldGrammar$1
|
|
1124
|
+
}),
|
|
1125
|
+
createNameParslet({ allowedAdditionalTokens: [
|
|
1126
|
+
"event",
|
|
1127
|
+
"external",
|
|
1128
|
+
"in"
|
|
1129
|
+
] }),
|
|
1130
|
+
typeOfParslet,
|
|
1131
|
+
createFunctionParslet({
|
|
1132
|
+
allowWithoutParenthesis: false,
|
|
1133
|
+
allowNamedParameters: ["this", "new"],
|
|
1134
|
+
allowNoReturnType: true,
|
|
1135
|
+
allowNewAsFunctionKeyword: false
|
|
1136
|
+
}),
|
|
1137
|
+
createVariadicParslet({
|
|
1138
|
+
allowEnclosingBrackets: false,
|
|
1139
|
+
allowPostfix: false
|
|
1140
|
+
}),
|
|
1141
|
+
createNameParslet({ allowedAdditionalTokens: ["keyof"] }),
|
|
1142
|
+
createSpecialNamePathParslet({
|
|
1143
|
+
allowedTypes: ["module"],
|
|
1144
|
+
pathGrammar
|
|
1145
|
+
}),
|
|
1146
|
+
createNamePathParslet({
|
|
1147
|
+
allowSquareBracketsOnAnyType: false,
|
|
1148
|
+
allowJsdocNamePaths: true,
|
|
1149
|
+
pathGrammar
|
|
1150
|
+
}),
|
|
1151
|
+
createKeyValueParslet({
|
|
1152
|
+
allowOptional: false,
|
|
1153
|
+
allowVariadic: false
|
|
1154
|
+
}),
|
|
1155
|
+
symbolParslet
|
|
1156
|
+
];
|
|
1157
|
+
const closureNameGrammar = [
|
|
1158
|
+
genericParslet,
|
|
1159
|
+
arrayBracketsParslet,
|
|
1160
|
+
createNameParslet({ allowedAdditionalTokens: baseNameTokens })
|
|
1161
|
+
];
|
|
1162
|
+
const closureNamePathGrammar = [
|
|
1163
|
+
genericParslet,
|
|
1164
|
+
arrayBracketsParslet,
|
|
1165
|
+
createNameParslet({ allowedAdditionalTokens: baseNameTokens }),
|
|
1166
|
+
createNamePathParslet({
|
|
1167
|
+
allowSquareBracketsOnAnyType: false,
|
|
1168
|
+
allowJsdocNamePaths: true,
|
|
1169
|
+
pathGrammar
|
|
1170
|
+
})
|
|
1171
|
+
];
|
|
1172
|
+
const closureNamePathSpecialGrammar = [createSpecialNamePathParslet({
|
|
1173
|
+
allowedTypes: ["module"],
|
|
1174
|
+
pathGrammar
|
|
1175
|
+
}), ...closureNamePathGrammar];
|
|
1176
|
+
//#endregion
|
|
1177
|
+
//#region src/parslets/assertsParslet.ts
|
|
1178
|
+
const assertsParslet = composeParslet({
|
|
1179
|
+
name: "assertsParslet",
|
|
1180
|
+
accept: (type) => type === "asserts",
|
|
1181
|
+
parsePrefix: (parser) => {
|
|
1182
|
+
parser.consume("asserts");
|
|
1183
|
+
const left = parser.parseIntermediateType(10);
|
|
1184
|
+
if (left.type !== "JsdocTypeName") throw new UnexpectedTypeError(left, "A typescript asserts always has to have a name.");
|
|
1185
|
+
if (!parser.consume("is")) return {
|
|
1186
|
+
type: "JsdocTypeAssertsPlain",
|
|
1187
|
+
element: left
|
|
1188
|
+
};
|
|
1189
|
+
return {
|
|
1190
|
+
type: "JsdocTypeAsserts",
|
|
1191
|
+
left,
|
|
1192
|
+
right: assertRootResult(parser.parseIntermediateType(8))
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
//#endregion
|
|
1197
|
+
//#region src/parslets/FunctionPropertyParslet.ts
|
|
1198
|
+
const functionPropertyParslet = composeParslet({
|
|
1199
|
+
name: "functionPropertyParslet",
|
|
1200
|
+
accept: (type, next) => type === "new" && (next === "(" || next === "<") || type === "Identifier" && (next === "(" || next === "<") || type === "StringValue" && (next === "(" || next === "<") || type === "(" || type === "<",
|
|
1201
|
+
parsePrefix: (parser) => {
|
|
1202
|
+
let result;
|
|
1203
|
+
const returnType = {
|
|
1204
|
+
type: "JsdocTypeName",
|
|
1205
|
+
value: "void"
|
|
1206
|
+
};
|
|
1207
|
+
if (parser.consume("new")) result = {
|
|
1208
|
+
type: "JsdocTypeConstructorSignature",
|
|
1209
|
+
parameters: [],
|
|
1210
|
+
returnType
|
|
1211
|
+
};
|
|
1212
|
+
else {
|
|
1213
|
+
const text = parser.lexer.current.text;
|
|
1214
|
+
if (parser.consume("Identifier")) result = {
|
|
1215
|
+
type: "JsdocTypeMethodSignature",
|
|
1216
|
+
name: text,
|
|
1217
|
+
meta: { quote: void 0 },
|
|
1218
|
+
parameters: [],
|
|
1219
|
+
returnType
|
|
1220
|
+
};
|
|
1221
|
+
else {
|
|
1222
|
+
const text = parser.lexer.current.text;
|
|
1223
|
+
if (parser.consume("StringValue")) result = {
|
|
1224
|
+
type: "JsdocTypeMethodSignature",
|
|
1225
|
+
name: text.slice(1, -1),
|
|
1226
|
+
meta: { quote: text.startsWith("\"") ? "double" : "single" },
|
|
1227
|
+
parameters: [],
|
|
1228
|
+
returnType
|
|
1229
|
+
};
|
|
1230
|
+
else result = {
|
|
1231
|
+
type: "JsdocTypeCallSignature",
|
|
1232
|
+
parameters: [],
|
|
1233
|
+
returnType
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
const typeParameters = [];
|
|
1238
|
+
if (parser.consume("<")) {
|
|
1239
|
+
do {
|
|
1240
|
+
let defaultValue = void 0;
|
|
1241
|
+
let name = parser.parseIntermediateType(10);
|
|
1242
|
+
if (name.type === "JsdocTypeOptional") {
|
|
1243
|
+
name = name.element;
|
|
1244
|
+
defaultValue = parser.parseType(10);
|
|
1245
|
+
}
|
|
1246
|
+
if (name.type !== "JsdocTypeName") throw new UnexpectedTypeError(name);
|
|
1247
|
+
let constraint = void 0;
|
|
1248
|
+
if (parser.consume("extends")) {
|
|
1249
|
+
constraint = parser.parseType(10);
|
|
1250
|
+
if (constraint.type === "JsdocTypeOptional") {
|
|
1251
|
+
constraint = constraint.element;
|
|
1252
|
+
defaultValue = parser.parseType(10);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
const typeParameter = {
|
|
1256
|
+
type: "JsdocTypeTypeParameter",
|
|
1257
|
+
name
|
|
1258
|
+
};
|
|
1259
|
+
if (constraint !== void 0) typeParameter.constraint = constraint;
|
|
1260
|
+
if (defaultValue !== void 0) typeParameter.defaultValue = defaultValue;
|
|
1261
|
+
typeParameters.push(typeParameter);
|
|
1262
|
+
if (parser.consume(">")) break;
|
|
1263
|
+
} while (parser.consume(","));
|
|
1264
|
+
result.typeParameters = typeParameters;
|
|
1265
|
+
}
|
|
1266
|
+
/* c8 ignore next 3 -- Unreachable */
|
|
1267
|
+
if (!(parser.lexer.current.type === "(")) throw new Error("function property is missing parameter list");
|
|
1268
|
+
return result;
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
//#endregion
|
|
1272
|
+
//#region src/parslets/TupleParslet.ts
|
|
1273
|
+
function createTupleParslet({ allowQuestionMark }) {
|
|
1274
|
+
return composeParslet({
|
|
1275
|
+
name: "tupleParslet",
|
|
1276
|
+
accept: (type) => type === "[",
|
|
1277
|
+
parsePrefix: (parser) => {
|
|
1278
|
+
parser.consume("[");
|
|
1279
|
+
const result = {
|
|
1280
|
+
type: "JsdocTypeTuple",
|
|
1281
|
+
elements: []
|
|
1282
|
+
};
|
|
1283
|
+
if (parser.consume("]")) return result;
|
|
1284
|
+
const typeList = parser.parseIntermediateType(0);
|
|
1285
|
+
if (typeList.type === "JsdocTypeParameterList") if (typeList.elements[0].type === "JsdocTypeKeyValue") result.elements = typeList.elements.map(assertPlainKeyValueResult);
|
|
1286
|
+
else result.elements = typeList.elements.map(assertRootResult);
|
|
1287
|
+
else if (typeList.type === "JsdocTypeKeyValue") result.elements = [assertPlainKeyValueResult(typeList)];
|
|
1288
|
+
else result.elements = [assertRootResult(typeList)];
|
|
1289
|
+
if (!parser.consume("]")) throw new Error("Unterminated '['");
|
|
1290
|
+
if (!allowQuestionMark && result.elements.some((e) => e.type === "JsdocTypeUnknown")) throw new Error("Question mark in tuple not allowed");
|
|
1291
|
+
return result;
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
//#endregion
|
|
1296
|
+
//#region src/parslets/KeyOfParslet.ts
|
|
1297
|
+
const keyOfParslet = composeParslet({
|
|
1298
|
+
name: "keyOfParslet",
|
|
1299
|
+
accept: (type) => type === "keyof",
|
|
1300
|
+
parsePrefix: (parser) => {
|
|
1301
|
+
parser.consume("keyof");
|
|
1302
|
+
return {
|
|
1303
|
+
type: "JsdocTypeKeyof",
|
|
1304
|
+
element: assertRootResult(parser.parseType(13))
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
//#endregion
|
|
1309
|
+
//#region src/parslets/InferParslet.ts
|
|
1310
|
+
const inferParslet = composeParslet({
|
|
1311
|
+
name: "inferParslet",
|
|
1312
|
+
accept: (type) => type === "infer",
|
|
1313
|
+
parsePrefix: (parser) => {
|
|
1314
|
+
parser.consume("infer");
|
|
1315
|
+
const element = parser.parseIntermediateType(12);
|
|
1316
|
+
if (element.type !== "JsdocTypeName") throw new UnexpectedTypeError(element, "A typescript infer always has to have a name.");
|
|
1317
|
+
return {
|
|
1318
|
+
type: "JsdocTypeInfer",
|
|
1319
|
+
element
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
//#endregion
|
|
1324
|
+
//#region src/parslets/ImportParslet.ts
|
|
1325
|
+
const importParslet = composeParslet({
|
|
1326
|
+
name: "importParslet",
|
|
1327
|
+
accept: (type) => type === "import",
|
|
1328
|
+
parsePrefix: (parser) => {
|
|
1329
|
+
parser.consume("import");
|
|
1330
|
+
if (!parser.consume("(")) throw new Error("Missing parenthesis after import keyword");
|
|
1331
|
+
const path = parser.parseType(7);
|
|
1332
|
+
if (path.type !== "JsdocTypeStringValue") throw new Error("Only string values are allowed as paths for imports");
|
|
1333
|
+
if (!parser.consume(")")) throw new Error("Missing closing parenthesis after import keyword");
|
|
1334
|
+
return {
|
|
1335
|
+
type: "JsdocTypeImport",
|
|
1336
|
+
element: path
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
//#endregion
|
|
1341
|
+
//#region src/parslets/ReadonlyPropertyParslet.ts
|
|
1342
|
+
const readonlyPropertyParslet = composeParslet({
|
|
1343
|
+
name: "readonlyPropertyParslet",
|
|
1344
|
+
accept: (type, next) => type === "readonly" && next !== ":" && next !== "?",
|
|
1345
|
+
parsePrefix: (parser) => {
|
|
1346
|
+
parser.consume("readonly");
|
|
1347
|
+
return {
|
|
1348
|
+
type: "JsdocTypeReadonlyProperty",
|
|
1349
|
+
element: parser.parseIntermediateType(3)
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
//#endregion
|
|
1354
|
+
//#region src/parslets/ArrowFunctionParslet.ts
|
|
1355
|
+
const arrowFunctionParslet = composeParslet({
|
|
1356
|
+
name: "arrowFunctionParslet",
|
|
1357
|
+
precedence: 15,
|
|
1358
|
+
accept: (type) => type === "=>",
|
|
1359
|
+
parseInfix: (parser, left) => {
|
|
1360
|
+
parser.consume("=>");
|
|
1361
|
+
return {
|
|
1362
|
+
type: "JsdocTypeFunction",
|
|
1363
|
+
parameters: getParameters(left).map(assertPlainKeyValueOrNameResult),
|
|
1364
|
+
arrow: true,
|
|
1365
|
+
constructor: false,
|
|
1366
|
+
parenthesis: true,
|
|
1367
|
+
returnType: parser.parseType(2)
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
//#endregion
|
|
1372
|
+
//#region src/parslets/GenericArrowFunctionParslet.ts
|
|
1373
|
+
const genericArrowFunctionParslet = composeParslet({
|
|
1374
|
+
name: "genericArrowFunctionParslet",
|
|
1375
|
+
accept: (type) => type === "<",
|
|
1376
|
+
parsePrefix: (parser) => {
|
|
1377
|
+
const typeParameters = [];
|
|
1378
|
+
parser.consume("<");
|
|
1379
|
+
do {
|
|
1380
|
+
let defaultValue = void 0;
|
|
1381
|
+
let name = parser.parseIntermediateType(10);
|
|
1382
|
+
if (name.type === "JsdocTypeOptional") {
|
|
1383
|
+
name = name.element;
|
|
1384
|
+
defaultValue = parser.parseType(10);
|
|
1385
|
+
}
|
|
1386
|
+
if (name.type !== "JsdocTypeName") throw new UnexpectedTypeError(name);
|
|
1387
|
+
let constraint = void 0;
|
|
1388
|
+
if (parser.consume("extends")) {
|
|
1389
|
+
constraint = parser.parseType(10);
|
|
1390
|
+
if (constraint.type === "JsdocTypeOptional") {
|
|
1391
|
+
constraint = constraint.element;
|
|
1392
|
+
defaultValue = parser.parseType(10);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
const typeParameter = {
|
|
1396
|
+
type: "JsdocTypeTypeParameter",
|
|
1397
|
+
name
|
|
1398
|
+
};
|
|
1399
|
+
if (constraint !== void 0) typeParameter.constraint = constraint;
|
|
1400
|
+
if (defaultValue !== void 0) typeParameter.defaultValue = defaultValue;
|
|
1401
|
+
typeParameters.push(typeParameter);
|
|
1402
|
+
if (parser.consume(">")) break;
|
|
1403
|
+
} while (parser.consume(","));
|
|
1404
|
+
const functionBase = parser.parseIntermediateType(10);
|
|
1405
|
+
functionBase.typeParameters = typeParameters;
|
|
1406
|
+
return functionBase;
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
//#endregion
|
|
1410
|
+
//#region src/parslets/IntersectionParslet.ts
|
|
1411
|
+
const intersectionParslet = composeParslet({
|
|
1412
|
+
name: "intersectionParslet",
|
|
1413
|
+
accept: (type) => type === "&",
|
|
1414
|
+
precedence: 6,
|
|
1415
|
+
parseInfix: (parser, left) => {
|
|
1416
|
+
parser.consume("&");
|
|
1417
|
+
const elements = [];
|
|
1418
|
+
do
|
|
1419
|
+
elements.push(parser.parseType(6));
|
|
1420
|
+
while (parser.consume("&"));
|
|
1421
|
+
return {
|
|
1422
|
+
type: "JsdocTypeIntersection",
|
|
1423
|
+
elements: [assertResultIsNotReservedWord(parser, assertRootResult(left)), ...elements.map((element) => assertResultIsNotReservedWord(parser, element))]
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
});
|
|
1427
|
+
//#endregion
|
|
1428
|
+
//#region src/parslets/predicateParslet.ts
|
|
1429
|
+
const predicateParslet = composeParslet({
|
|
1430
|
+
name: "predicateParslet",
|
|
1431
|
+
precedence: 8,
|
|
1432
|
+
accept: (type) => type === "is",
|
|
1433
|
+
parseInfix: (parser, left) => {
|
|
1434
|
+
if (left.type !== "JsdocTypeName") throw new UnexpectedTypeError(left, "A typescript predicate always has to have a name on the left side.");
|
|
1435
|
+
parser.consume("is");
|
|
1436
|
+
return {
|
|
1437
|
+
type: "JsdocTypePredicate",
|
|
1438
|
+
left,
|
|
1439
|
+
right: assertRootResult(parser.parseIntermediateType(8))
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
//#endregion
|
|
1444
|
+
//#region src/lexer/Lexer.ts
|
|
1445
|
+
const breakingWhitespaceRegex = /^\s*\n\s*/v;
|
|
1446
|
+
var Lexer = class Lexer {
|
|
1447
|
+
text = "";
|
|
1448
|
+
lexerRules;
|
|
1449
|
+
current;
|
|
1450
|
+
next;
|
|
1451
|
+
previous;
|
|
1452
|
+
static create(lexerRules, text) {
|
|
1453
|
+
const current = this.read(lexerRules, text);
|
|
1454
|
+
text = current.text;
|
|
1455
|
+
const next = this.read(lexerRules, text);
|
|
1456
|
+
text = next.text;
|
|
1457
|
+
return new Lexer(lexerRules, text, void 0, current.token, next.token);
|
|
1458
|
+
}
|
|
1459
|
+
constructor(lexerRules, text, previous, current, next) {
|
|
1460
|
+
this.lexerRules = lexerRules;
|
|
1461
|
+
this.text = text;
|
|
1462
|
+
this.previous = previous;
|
|
1463
|
+
this.current = current;
|
|
1464
|
+
this.next = next;
|
|
1465
|
+
}
|
|
1466
|
+
static read(lexerRules, text, startOfLine = false) {
|
|
1467
|
+
startOfLine ||= breakingWhitespaceRegex.test(text);
|
|
1468
|
+
const start = text.length;
|
|
1469
|
+
const initialWhitespace = /^\s+/v.exec(text)?.[0] ?? "";
|
|
1470
|
+
text = text.trimStart();
|
|
1471
|
+
const trimmed = start - text.length;
|
|
1472
|
+
for (const rule of lexerRules) {
|
|
1473
|
+
const partial = rule(text);
|
|
1474
|
+
if (partial !== null) {
|
|
1475
|
+
const initialLines = initialWhitespace.split("\n");
|
|
1476
|
+
const currentLines = partial.text.split("\n");
|
|
1477
|
+
const token = {
|
|
1478
|
+
...partial,
|
|
1479
|
+
startOfLine,
|
|
1480
|
+
reduced: trimmed + partial.text.length,
|
|
1481
|
+
line: initialLines.length + currentLines.length - 2,
|
|
1482
|
+
column: currentLines.length === 1 ? (initialLines.at(-1)?.length ?? 0) + (currentLines.at(-1)?.length ?? 0) : currentLines.at(-1)?.length ?? 0
|
|
1483
|
+
};
|
|
1484
|
+
text = text.slice(token.text.length);
|
|
1485
|
+
return {
|
|
1486
|
+
text,
|
|
1487
|
+
token
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
throw new Error(`Unexpected Token ${text}`);
|
|
1492
|
+
}
|
|
1493
|
+
remaining() {
|
|
1494
|
+
return this.next.text + this.text;
|
|
1495
|
+
}
|
|
1496
|
+
advance() {
|
|
1497
|
+
const next = Lexer.read(this.lexerRules, this.text);
|
|
1498
|
+
return new Lexer(this.lexerRules, next.text, this.current, this.next, next.token);
|
|
1499
|
+
}
|
|
1500
|
+
};
|
|
1501
|
+
//#endregion
|
|
1502
|
+
//#region src/parslets/ObjectSquaredPropertyParslet.ts
|
|
1503
|
+
const objectSquaredPropertyParslet = composeParslet({
|
|
1504
|
+
name: "objectSquarePropertyParslet",
|
|
1505
|
+
accept: (type) => type === "[",
|
|
1506
|
+
parsePrefix: (parser) => {
|
|
1507
|
+
if (parser.baseParser === void 0) throw new Error("Only allowed inside object grammar");
|
|
1508
|
+
parser.consume("[");
|
|
1509
|
+
let innerBracketType;
|
|
1510
|
+
if (parser.externalParsers?.computedPropertyParser === void 0) try {
|
|
1511
|
+
innerBracketType = parser.parseIntermediateType(2);
|
|
1512
|
+
} catch (err) {
|
|
1513
|
+
throw new Error("Error parsing value inside square bracketed property.", { cause: err });
|
|
1514
|
+
}
|
|
1515
|
+
let result;
|
|
1516
|
+
if (innerBracketType?.type === "JsdocTypeObjectField" && typeof innerBracketType.key === "string" && !innerBracketType.optional && !innerBracketType.readonly && innerBracketType.right !== void 0) {
|
|
1517
|
+
const key = innerBracketType.key;
|
|
1518
|
+
if (!parser.consume("]")) throw new Error("Unterminated square brackets");
|
|
1519
|
+
if (!parser.consume(":")) throw new Error("Incomplete index signature");
|
|
1520
|
+
const parentParser = parser.baseParser;
|
|
1521
|
+
parentParser.acceptLexerState(parser);
|
|
1522
|
+
innerBracketType.key = {
|
|
1523
|
+
type: "JsdocTypeIndexSignature",
|
|
1524
|
+
key,
|
|
1525
|
+
right: innerBracketType.right
|
|
1526
|
+
};
|
|
1527
|
+
innerBracketType.optional = false;
|
|
1528
|
+
innerBracketType.meta.quote = void 0;
|
|
1529
|
+
result = innerBracketType;
|
|
1530
|
+
const right = parentParser.parseType(4);
|
|
1531
|
+
result.right = right;
|
|
1532
|
+
parser.acceptLexerState(parentParser);
|
|
1533
|
+
} else if (innerBracketType?.type === "JsdocTypeName" && parser.consume("in")) {
|
|
1534
|
+
const parentParser = parser.baseParser;
|
|
1535
|
+
parentParser.acceptLexerState(parser);
|
|
1536
|
+
const mappedTypeRight = parentParser.parseType(4);
|
|
1537
|
+
if (!parentParser.consume("]")) throw new Error("Unterminated square brackets");
|
|
1538
|
+
const optional = parentParser.consume("?");
|
|
1539
|
+
if (!parentParser.consume(":")) throw new Error("Incomplete mapped type clause: missing colon");
|
|
1540
|
+
const right = parentParser.parseType(4);
|
|
1541
|
+
result = {
|
|
1542
|
+
type: "JsdocTypeObjectField",
|
|
1543
|
+
optional,
|
|
1544
|
+
readonly: false,
|
|
1545
|
+
meta: { quote: void 0 },
|
|
1546
|
+
key: {
|
|
1547
|
+
type: "JsdocTypeMappedType",
|
|
1548
|
+
key: innerBracketType.value,
|
|
1549
|
+
right: mappedTypeRight
|
|
1550
|
+
},
|
|
1551
|
+
right
|
|
1552
|
+
};
|
|
1553
|
+
parser.acceptLexerState(parentParser);
|
|
1554
|
+
} else {
|
|
1555
|
+
if (parser.externalParsers?.computedPropertyParser !== void 0) {
|
|
1556
|
+
let remaining = parser.lexer.current.text + parser.lexer.remaining();
|
|
1557
|
+
let checkingText = remaining;
|
|
1558
|
+
while (checkingText !== "") {
|
|
1559
|
+
try {
|
|
1560
|
+
innerBracketType = parser.externalParsers.computedPropertyParser(checkingText);
|
|
1561
|
+
break;
|
|
1562
|
+
} catch (err) {}
|
|
1563
|
+
checkingText = checkingText.slice(0, -1);
|
|
1564
|
+
}
|
|
1565
|
+
remaining = remaining.slice(checkingText.length);
|
|
1566
|
+
const remainingTextParser = new Parser(parser.grammar, Lexer.create(parser.lexer.lexerRules, remaining), parser.baseParser, { externalParsers: { computedPropertyParser: parser.externalParsers.computedPropertyParser } });
|
|
1567
|
+
parser.acceptLexerState(remainingTextParser);
|
|
1568
|
+
}
|
|
1569
|
+
if (!parser.consume("]")) throw new Error("Unterminated square brackets");
|
|
1570
|
+
let optional = parser.consume("?");
|
|
1571
|
+
const typeParameters = [];
|
|
1572
|
+
if (parser.consume("<")) do {
|
|
1573
|
+
let defaultValue = void 0;
|
|
1574
|
+
let name = parser.parseIntermediateType(10);
|
|
1575
|
+
if (name.type === "JsdocTypeOptional") {
|
|
1576
|
+
name = name.element;
|
|
1577
|
+
defaultValue = parser.parseType(10);
|
|
1578
|
+
}
|
|
1579
|
+
if (name.type !== "JsdocTypeName") throw new UnexpectedTypeError(name);
|
|
1580
|
+
let constraint = void 0;
|
|
1581
|
+
if (parser.consume("extends")) {
|
|
1582
|
+
constraint = parser.parseType(10);
|
|
1583
|
+
if (constraint.type === "JsdocTypeOptional") {
|
|
1584
|
+
constraint = constraint.element;
|
|
1585
|
+
defaultValue = parser.parseType(10);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
const typeParameter = {
|
|
1589
|
+
type: "JsdocTypeTypeParameter",
|
|
1590
|
+
name
|
|
1591
|
+
};
|
|
1592
|
+
if (constraint !== void 0) typeParameter.constraint = constraint;
|
|
1593
|
+
if (defaultValue !== void 0) typeParameter.defaultValue = defaultValue;
|
|
1594
|
+
typeParameters.push(typeParameter);
|
|
1595
|
+
if (parser.consume(">")) break;
|
|
1596
|
+
} while (parser.consume(","));
|
|
1597
|
+
let type;
|
|
1598
|
+
let key;
|
|
1599
|
+
const checkMiddle = () => {
|
|
1600
|
+
if (!optional) optional = parser.consume("?");
|
|
1601
|
+
};
|
|
1602
|
+
let right;
|
|
1603
|
+
if (parser.lexer.current.type === "(") {
|
|
1604
|
+
const signatureParser = new Parser([createKeyValueParslet({
|
|
1605
|
+
allowVariadic: true,
|
|
1606
|
+
allowOptional: true,
|
|
1607
|
+
acceptParameterList: true
|
|
1608
|
+
}), ...parser.baseParser.grammar.flatMap((grammar) => {
|
|
1609
|
+
if (grammar.name === "keyValueParslet") return [];
|
|
1610
|
+
return [grammar];
|
|
1611
|
+
})], parser.lexer, parser);
|
|
1612
|
+
signatureParser.acceptLexerState(parser);
|
|
1613
|
+
const params = signatureParser.parseIntermediateType(2);
|
|
1614
|
+
parser.acceptLexerState(signatureParser);
|
|
1615
|
+
const parameters = getParameters(params);
|
|
1616
|
+
type = "JsdocTypeComputedMethod";
|
|
1617
|
+
checkMiddle();
|
|
1618
|
+
parser.consume(":");
|
|
1619
|
+
const nextValue = parser.parseType(4);
|
|
1620
|
+
key = {
|
|
1621
|
+
type,
|
|
1622
|
+
optional,
|
|
1623
|
+
value: innerBracketType,
|
|
1624
|
+
parameters,
|
|
1625
|
+
returnType: nextValue
|
|
1626
|
+
};
|
|
1627
|
+
if (typeParameters.length > 0) key.typeParameters = typeParameters;
|
|
1628
|
+
} else {
|
|
1629
|
+
type = "JsdocTypeComputedProperty";
|
|
1630
|
+
checkMiddle();
|
|
1631
|
+
if (!parser.consume(":")) throw new Error("Incomplete computed property: missing colon");
|
|
1632
|
+
right = parser.parseType(4);
|
|
1633
|
+
key = {
|
|
1634
|
+
type,
|
|
1635
|
+
value: innerBracketType
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
result = {
|
|
1639
|
+
type: "JsdocTypeObjectField",
|
|
1640
|
+
optional: type === "JsdocTypeComputedMethod" ? false : optional,
|
|
1641
|
+
readonly: false,
|
|
1642
|
+
meta: { quote: void 0 },
|
|
1643
|
+
key,
|
|
1644
|
+
right
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
return result;
|
|
1648
|
+
}
|
|
1649
|
+
});
|
|
1650
|
+
//#endregion
|
|
1651
|
+
//#region src/parslets/ReadonlyArrayParslet.ts
|
|
1652
|
+
const readonlyArrayParslet = composeParslet({
|
|
1653
|
+
name: "readonlyArrayParslet",
|
|
1654
|
+
accept: (type) => type === "readonly",
|
|
1655
|
+
parsePrefix: (parser) => {
|
|
1656
|
+
parser.consume("readonly");
|
|
1657
|
+
return {
|
|
1658
|
+
type: "JsdocTypeReadonlyArray",
|
|
1659
|
+
element: assertArrayOrTupleResult(parser.parseIntermediateType(0))
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
//#endregion
|
|
1664
|
+
//#region src/parslets/ConditionalParslet.ts
|
|
1665
|
+
const conditionalParslet = composeParslet({
|
|
1666
|
+
name: "conditionalParslet",
|
|
1667
|
+
precedence: 8,
|
|
1668
|
+
accept: (type) => type === "extends",
|
|
1669
|
+
parseInfix: (parser, left) => {
|
|
1670
|
+
parser.consume("extends");
|
|
1671
|
+
const extendsType = assertRootResult(parser.parseType(13));
|
|
1672
|
+
parser.consume("?");
|
|
1673
|
+
const trueType = parser.parseType(8);
|
|
1674
|
+
parser.consume(":");
|
|
1675
|
+
return {
|
|
1676
|
+
type: "JsdocTypeConditional",
|
|
1677
|
+
checksType: assertRootResult(left),
|
|
1678
|
+
extendsType,
|
|
1679
|
+
trueType,
|
|
1680
|
+
falseType: parser.parseType(8)
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
});
|
|
1684
|
+
//#endregion
|
|
1685
|
+
//#region src/lexer/LexerRules.ts
|
|
1686
|
+
function makePunctuationRule(type) {
|
|
1687
|
+
return (text) => {
|
|
1688
|
+
if (text.startsWith(type)) return {
|
|
1689
|
+
type,
|
|
1690
|
+
text: type
|
|
1691
|
+
};
|
|
1692
|
+
else return null;
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
function getQuoted(text) {
|
|
1696
|
+
let position = 0;
|
|
1697
|
+
let char = void 0;
|
|
1698
|
+
const mark = text[0];
|
|
1699
|
+
let escaped = false;
|
|
1700
|
+
if (mark !== "'" && mark !== "\"") return null;
|
|
1701
|
+
while (position < text.length) {
|
|
1702
|
+
position++;
|
|
1703
|
+
char = text[position];
|
|
1704
|
+
if (!escaped && char === mark) {
|
|
1705
|
+
position++;
|
|
1706
|
+
break;
|
|
1707
|
+
}
|
|
1708
|
+
escaped = !escaped && char === "\\";
|
|
1709
|
+
}
|
|
1710
|
+
if (char !== mark) throw new Error("Unterminated String");
|
|
1711
|
+
return text.slice(0, position);
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* Gets a full template literal (enclosed in backticks)
|
|
1715
|
+
*/
|
|
1716
|
+
function getTemplateLiteral(text) {
|
|
1717
|
+
let position = 0;
|
|
1718
|
+
let char = void 0;
|
|
1719
|
+
const mark = text[0];
|
|
1720
|
+
let escaped = false;
|
|
1721
|
+
if (mark !== "`") return null;
|
|
1722
|
+
while (position < text.length) {
|
|
1723
|
+
position++;
|
|
1724
|
+
char = text[position];
|
|
1725
|
+
if (!escaped && char === mark) {
|
|
1726
|
+
position++;
|
|
1727
|
+
break;
|
|
1728
|
+
}
|
|
1729
|
+
escaped = !escaped && char === "\\";
|
|
1730
|
+
}
|
|
1731
|
+
if (char !== mark) throw new Error("Unterminated template literal");
|
|
1732
|
+
return text.slice(0, position);
|
|
1733
|
+
}
|
|
1734
|
+
/**
|
|
1735
|
+
* Gets the next literal (non-interpolation) portion of a text
|
|
1736
|
+
*/
|
|
1737
|
+
function getTemplateLiteralLiteral(text) {
|
|
1738
|
+
let position = 0;
|
|
1739
|
+
const start = text[0];
|
|
1740
|
+
let escaped = false;
|
|
1741
|
+
if (start === "`" || start === "$" && text[1] === "{") return null;
|
|
1742
|
+
while (position < text.length) {
|
|
1743
|
+
position++;
|
|
1744
|
+
const char = text[position];
|
|
1745
|
+
if (!escaped && (char === "`" || char === "$" && text[position + 1] === "{")) break;
|
|
1746
|
+
escaped = !escaped && char === "\\";
|
|
1747
|
+
}
|
|
1748
|
+
return text.slice(0, position);
|
|
1749
|
+
}
|
|
1750
|
+
const identifierStartRegex = /[$_\p{ID_Start}]|\\u\p{Hex_Digit}{4}|\\u\{0*(?:\p{Hex_Digit}{1,5}|10\p{Hex_Digit}{4})\}/v;
|
|
1751
|
+
const identifierContinueRegex = /[$\p{ID_Continue}\u200C\u200D]|\\u\p{Hex_Digit}{4}|\\u\{0*(?:\p{Hex_Digit}{1,5}|10\p{Hex_Digit}{4})\}/v;
|
|
1752
|
+
const identifierContinueRegexLoose = /[$\-\p{ID_Continue}\u200C\u200D]|\\u\p{Hex_Digit}{4}|\\u\{0*(?:\p{Hex_Digit}{1,5}|10\p{Hex_Digit}{4})\}/v;
|
|
1753
|
+
function makeGetIdentifier(identifierContinueRegex) {
|
|
1754
|
+
return function(text) {
|
|
1755
|
+
let char = text[0];
|
|
1756
|
+
if (!identifierStartRegex.test(char)) return null;
|
|
1757
|
+
let position = 1;
|
|
1758
|
+
do {
|
|
1759
|
+
char = text[position];
|
|
1760
|
+
if (!identifierContinueRegex.test(char)) break;
|
|
1761
|
+
position++;
|
|
1762
|
+
} while (position < text.length);
|
|
1763
|
+
return text.slice(0, position);
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
const bigintRegex = /^(?:-?\d+n)/v;
|
|
1767
|
+
const numberRegex = /^(?:-?(?:(?:\d*\.\d+|\d+)(?:[Ee][+\-]?\d+)?))/v;
|
|
1768
|
+
const looseNumberRegex = /^(?:NaN|-?(?:(?:\d*\.\d+|\d+)(?:[Ee][+\-]?\d+)?|Infinity))/v;
|
|
1769
|
+
function getGetNumber(numberRegex) {
|
|
1770
|
+
return function getNumber(text) {
|
|
1771
|
+
return numberRegex.exec(text)?.[0] ?? null;
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
const looseIdentifierRule = (text) => {
|
|
1775
|
+
const value = makeGetIdentifier(identifierContinueRegexLoose)(text);
|
|
1776
|
+
if (value === null) return null;
|
|
1777
|
+
return {
|
|
1778
|
+
type: "Identifier",
|
|
1779
|
+
text: value
|
|
1780
|
+
};
|
|
1781
|
+
};
|
|
1782
|
+
const identifierRule = (text) => {
|
|
1783
|
+
const value = makeGetIdentifier(identifierContinueRegex)(text);
|
|
1784
|
+
if (value === null) return null;
|
|
1785
|
+
return {
|
|
1786
|
+
type: "Identifier",
|
|
1787
|
+
text: value
|
|
1788
|
+
};
|
|
1789
|
+
};
|
|
1790
|
+
function makeKeyWordRule(type) {
|
|
1791
|
+
return (text) => {
|
|
1792
|
+
if (!text.startsWith(type)) return null;
|
|
1793
|
+
const prepends = text[type.length];
|
|
1794
|
+
if (prepends !== void 0 && identifierContinueRegex.test(prepends)) return null;
|
|
1795
|
+
return {
|
|
1796
|
+
type,
|
|
1797
|
+
text: type
|
|
1798
|
+
};
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
const stringValueRule = (text) => {
|
|
1802
|
+
const value = getQuoted(text);
|
|
1803
|
+
if (value === null) return null;
|
|
1804
|
+
return {
|
|
1805
|
+
type: "StringValue",
|
|
1806
|
+
text: value
|
|
1807
|
+
};
|
|
1808
|
+
};
|
|
1809
|
+
const templateLiteralRule = (text) => {
|
|
1810
|
+
const value = getTemplateLiteral(text);
|
|
1811
|
+
if (value === null) return null;
|
|
1812
|
+
return {
|
|
1813
|
+
type: "TemplateLiteral",
|
|
1814
|
+
text: value
|
|
1815
|
+
};
|
|
1816
|
+
};
|
|
1817
|
+
const eofRule = (text) => {
|
|
1818
|
+
if (text.length > 0) return null;
|
|
1819
|
+
return {
|
|
1820
|
+
type: "EOF",
|
|
1821
|
+
text: ""
|
|
1822
|
+
};
|
|
1823
|
+
};
|
|
1824
|
+
const numberRule = (text) => {
|
|
1825
|
+
const bigintValue = bigintRegex.exec(text)?.[0];
|
|
1826
|
+
if (bigintValue !== void 0) return {
|
|
1827
|
+
type: "Number",
|
|
1828
|
+
text: bigintValue
|
|
1829
|
+
};
|
|
1830
|
+
const value = getGetNumber(numberRegex)(text);
|
|
1831
|
+
if (value === null) return null;
|
|
1832
|
+
return {
|
|
1833
|
+
type: "Number",
|
|
1834
|
+
text: value
|
|
1835
|
+
};
|
|
1836
|
+
};
|
|
1837
|
+
const looseNumberRule = (text) => {
|
|
1838
|
+
const bigintValue = bigintRegex.exec(text)?.[0];
|
|
1839
|
+
if (bigintValue !== void 0) return {
|
|
1840
|
+
type: "Number",
|
|
1841
|
+
text: bigintValue
|
|
1842
|
+
};
|
|
1843
|
+
const value = getGetNumber(looseNumberRegex)(text);
|
|
1844
|
+
if (value === null) return null;
|
|
1845
|
+
return {
|
|
1846
|
+
type: "Number",
|
|
1847
|
+
text: value
|
|
1848
|
+
};
|
|
1849
|
+
};
|
|
1850
|
+
/**
|
|
1851
|
+
* Will be processed highest precedence first
|
|
1852
|
+
*/
|
|
1853
|
+
const rules = [
|
|
1854
|
+
eofRule,
|
|
1855
|
+
makePunctuationRule("=>"),
|
|
1856
|
+
makePunctuationRule("("),
|
|
1857
|
+
makePunctuationRule(")"),
|
|
1858
|
+
makePunctuationRule("{"),
|
|
1859
|
+
makePunctuationRule("}"),
|
|
1860
|
+
makePunctuationRule("["),
|
|
1861
|
+
makePunctuationRule("]"),
|
|
1862
|
+
makePunctuationRule("|"),
|
|
1863
|
+
makePunctuationRule("&"),
|
|
1864
|
+
makePunctuationRule("<"),
|
|
1865
|
+
makePunctuationRule(">"),
|
|
1866
|
+
makePunctuationRule(","),
|
|
1867
|
+
makePunctuationRule(";"),
|
|
1868
|
+
makePunctuationRule("*"),
|
|
1869
|
+
makePunctuationRule("?"),
|
|
1870
|
+
makePunctuationRule("!"),
|
|
1871
|
+
makePunctuationRule("="),
|
|
1872
|
+
makePunctuationRule(":"),
|
|
1873
|
+
makePunctuationRule("..."),
|
|
1874
|
+
makePunctuationRule("."),
|
|
1875
|
+
makePunctuationRule("#"),
|
|
1876
|
+
makePunctuationRule("~"),
|
|
1877
|
+
makePunctuationRule("/"),
|
|
1878
|
+
makePunctuationRule("@"),
|
|
1879
|
+
makeKeyWordRule("undefined"),
|
|
1880
|
+
makeKeyWordRule("null"),
|
|
1881
|
+
makeKeyWordRule("function"),
|
|
1882
|
+
makeKeyWordRule("this"),
|
|
1883
|
+
makeKeyWordRule("new"),
|
|
1884
|
+
makeKeyWordRule("module"),
|
|
1885
|
+
makeKeyWordRule("event"),
|
|
1886
|
+
makeKeyWordRule("extends"),
|
|
1887
|
+
makeKeyWordRule("external"),
|
|
1888
|
+
makeKeyWordRule("infer"),
|
|
1889
|
+
makeKeyWordRule("typeof"),
|
|
1890
|
+
makeKeyWordRule("keyof"),
|
|
1891
|
+
makeKeyWordRule("readonly"),
|
|
1892
|
+
makeKeyWordRule("import"),
|
|
1893
|
+
makeKeyWordRule("is"),
|
|
1894
|
+
makeKeyWordRule("in"),
|
|
1895
|
+
makeKeyWordRule("asserts"),
|
|
1896
|
+
numberRule,
|
|
1897
|
+
identifierRule,
|
|
1898
|
+
stringValueRule,
|
|
1899
|
+
templateLiteralRule
|
|
1900
|
+
];
|
|
1901
|
+
const looseRules = rules.toSpliced(-4, 2, looseNumberRule, looseIdentifierRule);
|
|
1902
|
+
//#endregion
|
|
1903
|
+
//#region src/parslets/TemplateLiteralParslet.ts
|
|
1904
|
+
const templateLiteralParslet = composeParslet({
|
|
1905
|
+
name: "templateLiteralParslet",
|
|
1906
|
+
accept: (type) => type === "TemplateLiteral",
|
|
1907
|
+
parsePrefix: (parser) => {
|
|
1908
|
+
const text = parser.lexer.current.text;
|
|
1909
|
+
parser.consume("TemplateLiteral");
|
|
1910
|
+
const literals = [];
|
|
1911
|
+
const interpolations = [];
|
|
1912
|
+
let currentText = text.slice(1, -1);
|
|
1913
|
+
const advanceLiteral = () => {
|
|
1914
|
+
const literal = getTemplateLiteralLiteral(currentText) ?? "";
|
|
1915
|
+
literals.push(literal.replace(/\\`/gv, "`"));
|
|
1916
|
+
currentText = currentText.slice(literal.length);
|
|
1917
|
+
};
|
|
1918
|
+
advanceLiteral();
|
|
1919
|
+
while (true) {
|
|
1920
|
+
if (currentText.startsWith("${")) {
|
|
1921
|
+
currentText = currentText.slice(2);
|
|
1922
|
+
let templateParser;
|
|
1923
|
+
let interpolationType;
|
|
1924
|
+
let snipped = currentText;
|
|
1925
|
+
let remnant = "";
|
|
1926
|
+
while (true) try {
|
|
1927
|
+
templateParser = new Parser(parser.grammar, Lexer.create(parser.lexer.lexerRules, snipped));
|
|
1928
|
+
interpolationType = templateParser.parseType(0);
|
|
1929
|
+
break;
|
|
1930
|
+
} catch (err) {
|
|
1931
|
+
remnant = snipped.slice(-1) + remnant;
|
|
1932
|
+
snipped = snipped.slice(0, -1);
|
|
1933
|
+
}
|
|
1934
|
+
interpolations.push(interpolationType);
|
|
1935
|
+
if (templateParser.lexer.current.text !== "}") throw new Error("unterminated interpolation");
|
|
1936
|
+
currentText = templateParser.lexer.remaining() + remnant;
|
|
1937
|
+
} else break;
|
|
1938
|
+
advanceLiteral();
|
|
1939
|
+
}
|
|
1940
|
+
return {
|
|
1941
|
+
type: "JsdocTypeTemplateLiteral",
|
|
1942
|
+
literals,
|
|
1943
|
+
interpolations
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
});
|
|
1947
|
+
//#endregion
|
|
1948
|
+
//#region src/parslets/UniqueSymbolParslet.ts
|
|
1949
|
+
const uniqueSymbolParslet = (parser, _precedence, left) => {
|
|
1950
|
+
if (left !== null) return null;
|
|
1951
|
+
if (parser.lexer.current.type !== "Identifier" || parser.lexer.current.text !== "unique" || parser.lexer.next.type !== "Identifier" || parser.lexer.next.text !== "symbol") return null;
|
|
1952
|
+
parser.consume("Identifier");
|
|
1953
|
+
parser.consume("Identifier");
|
|
1954
|
+
return { type: "JsdocTypeUniqueSymbol" };
|
|
1955
|
+
};
|
|
1956
|
+
//#endregion
|
|
1957
|
+
//#region src/grammars/typescriptGrammar.ts
|
|
1958
|
+
const objectFieldGrammar = [
|
|
1959
|
+
functionPropertyParslet,
|
|
1960
|
+
readonlyPropertyParslet,
|
|
1961
|
+
createNameParslet({ allowedAdditionalTokens: baseNameTokens }),
|
|
1962
|
+
nullableParslet,
|
|
1963
|
+
optionalParslet,
|
|
1964
|
+
stringValueParslet,
|
|
1965
|
+
numberParslet,
|
|
1966
|
+
createObjectFieldParslet({
|
|
1967
|
+
allowSquaredProperties: true,
|
|
1968
|
+
allowKeyTypes: false,
|
|
1969
|
+
allowOptional: true,
|
|
1970
|
+
allowReadonly: true
|
|
1971
|
+
}),
|
|
1972
|
+
objectSquaredPropertyParslet
|
|
1973
|
+
];
|
|
1974
|
+
const typescriptGrammar = [
|
|
1975
|
+
...baseGrammar,
|
|
1976
|
+
createObjectParslet({
|
|
1977
|
+
allowKeyTypes: false,
|
|
1978
|
+
objectFieldGrammar,
|
|
1979
|
+
signatureGrammar: [createKeyValueParslet({
|
|
1980
|
+
allowVariadic: true,
|
|
1981
|
+
allowOptional: true,
|
|
1982
|
+
acceptParameterList: true
|
|
1983
|
+
})]
|
|
1984
|
+
}),
|
|
1985
|
+
readonlyArrayParslet,
|
|
1986
|
+
typeOfParslet,
|
|
1987
|
+
keyOfParslet,
|
|
1988
|
+
inferParslet,
|
|
1989
|
+
importParslet,
|
|
1990
|
+
stringValueParslet,
|
|
1991
|
+
createFunctionParslet({
|
|
1992
|
+
allowWithoutParenthesis: true,
|
|
1993
|
+
allowNoReturnType: true,
|
|
1994
|
+
allowNamedParameters: [
|
|
1995
|
+
"this",
|
|
1996
|
+
"new",
|
|
1997
|
+
"args"
|
|
1998
|
+
],
|
|
1999
|
+
allowNewAsFunctionKeyword: true
|
|
2000
|
+
}),
|
|
2001
|
+
createTupleParslet({ allowQuestionMark: false }),
|
|
2002
|
+
createVariadicParslet({
|
|
2003
|
+
allowEnclosingBrackets: false,
|
|
2004
|
+
allowPostfix: false
|
|
2005
|
+
}),
|
|
2006
|
+
assertsParslet,
|
|
2007
|
+
conditionalParslet,
|
|
2008
|
+
uniqueSymbolParslet,
|
|
2009
|
+
createNameParslet({ allowedAdditionalTokens: [
|
|
2010
|
+
"event",
|
|
2011
|
+
"external",
|
|
2012
|
+
"in"
|
|
2013
|
+
] }),
|
|
2014
|
+
createSpecialNamePathParslet({
|
|
2015
|
+
allowedTypes: ["module"],
|
|
2016
|
+
pathGrammar
|
|
2017
|
+
}),
|
|
2018
|
+
arrayBracketsParslet,
|
|
2019
|
+
arrowFunctionParslet,
|
|
2020
|
+
genericArrowFunctionParslet,
|
|
2021
|
+
createNamePathParslet({
|
|
2022
|
+
allowSquareBracketsOnAnyType: true,
|
|
2023
|
+
allowJsdocNamePaths: false,
|
|
2024
|
+
pathGrammar
|
|
2025
|
+
}),
|
|
2026
|
+
intersectionParslet,
|
|
2027
|
+
predicateParslet,
|
|
2028
|
+
templateLiteralParslet,
|
|
2029
|
+
createKeyValueParslet({
|
|
2030
|
+
allowVariadic: true,
|
|
2031
|
+
allowOptional: true
|
|
2032
|
+
})
|
|
2033
|
+
];
|
|
2034
|
+
const typescriptNameGrammar = [
|
|
2035
|
+
genericParslet,
|
|
2036
|
+
arrayBracketsParslet,
|
|
2037
|
+
createNameParslet({ allowedAdditionalTokens: baseNameTokens })
|
|
2038
|
+
];
|
|
2039
|
+
const typescriptNamePathGrammar = [
|
|
2040
|
+
genericParslet,
|
|
2041
|
+
arrayBracketsParslet,
|
|
2042
|
+
createNameParslet({ allowedAdditionalTokens: baseNameTokens }),
|
|
2043
|
+
createNamePathParslet({
|
|
2044
|
+
allowSquareBracketsOnAnyType: true,
|
|
2045
|
+
allowJsdocNamePaths: true,
|
|
2046
|
+
pathGrammar
|
|
2047
|
+
})
|
|
2048
|
+
];
|
|
2049
|
+
const typescriptNamePathSpecialGrammar = [createSpecialNamePathParslet({
|
|
2050
|
+
allowedTypes: ["module"],
|
|
2051
|
+
pathGrammar
|
|
2052
|
+
}), ...typescriptNamePathGrammar];
|
|
2053
|
+
//#endregion
|
|
2054
|
+
//#region src/parse.ts
|
|
2055
|
+
/**
|
|
2056
|
+
* This function parses the given expression in the given mode and produces a {@link RootResult}.
|
|
2057
|
+
* @param expression
|
|
2058
|
+
* @param mode
|
|
2059
|
+
*/
|
|
2060
|
+
function parse(expression, mode, { range = false, rangeStart, loc = false, locStart = {
|
|
2061
|
+
column: 0,
|
|
2062
|
+
line: 1
|
|
2063
|
+
}, module = true, strictMode = true, asyncFunctionBody = true, classContext = false, computedPropertyParser } = {}) {
|
|
2064
|
+
let parser;
|
|
2065
|
+
switch (mode) {
|
|
2066
|
+
case "closure":
|
|
2067
|
+
parser = new Parser(closureGrammar, Lexer.create(looseRules, expression), void 0, {
|
|
2068
|
+
module,
|
|
2069
|
+
strictMode,
|
|
2070
|
+
asyncFunctionBody,
|
|
2071
|
+
classContext,
|
|
2072
|
+
range,
|
|
2073
|
+
rangeStart,
|
|
2074
|
+
loc,
|
|
2075
|
+
locStart
|
|
2076
|
+
});
|
|
2077
|
+
break;
|
|
2078
|
+
case "jsdoc":
|
|
2079
|
+
parser = new Parser(jsdocGrammar, Lexer.create(looseRules, expression), void 0, {
|
|
2080
|
+
module,
|
|
2081
|
+
strictMode,
|
|
2082
|
+
asyncFunctionBody,
|
|
2083
|
+
classContext,
|
|
2084
|
+
range,
|
|
2085
|
+
rangeStart,
|
|
2086
|
+
loc,
|
|
2087
|
+
locStart
|
|
2088
|
+
});
|
|
2089
|
+
break;
|
|
2090
|
+
case "typescript":
|
|
2091
|
+
parser = new Parser(typescriptGrammar, Lexer.create(rules, expression), void 0, {
|
|
2092
|
+
module,
|
|
2093
|
+
strictMode,
|
|
2094
|
+
asyncFunctionBody,
|
|
2095
|
+
classContext,
|
|
2096
|
+
range,
|
|
2097
|
+
rangeStart,
|
|
2098
|
+
loc,
|
|
2099
|
+
locStart,
|
|
2100
|
+
externalParsers: { computedPropertyParser }
|
|
2101
|
+
});
|
|
2102
|
+
break;
|
|
2103
|
+
}
|
|
2104
|
+
const result = parser.parse();
|
|
2105
|
+
return assertResultIsNotReservedWord(parser, result);
|
|
2106
|
+
}
|
|
2107
|
+
/**
|
|
2108
|
+
* This function tries to parse the given expression in multiple modes and returns the first successful
|
|
2109
|
+
* {@link RootResult}. By default it tries `'typescript'`, `'closure'` and `'jsdoc'` in this order. If
|
|
2110
|
+
* no mode was successful it throws the error that was produced by the last parsing attempt.
|
|
2111
|
+
* @param expression
|
|
2112
|
+
* @param modes
|
|
2113
|
+
*/
|
|
2114
|
+
function tryParse(expression, modes = [
|
|
2115
|
+
"typescript",
|
|
2116
|
+
"closure",
|
|
2117
|
+
"jsdoc"
|
|
2118
|
+
], { module = true, strictMode = true, asyncFunctionBody = true, classContext = false, range, rangeStart, loc = false, locStart = {
|
|
2119
|
+
column: 0,
|
|
2120
|
+
line: 1
|
|
2121
|
+
} } = {}) {
|
|
2122
|
+
let error;
|
|
2123
|
+
for (const mode of modes) try {
|
|
2124
|
+
return parse(expression, mode, {
|
|
2125
|
+
module,
|
|
2126
|
+
strictMode,
|
|
2127
|
+
asyncFunctionBody,
|
|
2128
|
+
classContext,
|
|
2129
|
+
range,
|
|
2130
|
+
rangeStart,
|
|
2131
|
+
loc,
|
|
2132
|
+
locStart
|
|
2133
|
+
});
|
|
2134
|
+
} catch (e) {
|
|
2135
|
+
error = e;
|
|
2136
|
+
}
|
|
2137
|
+
throw error;
|
|
2138
|
+
}
|
|
2139
|
+
/**
|
|
2140
|
+
* This function parses the given expression in the given mode and produces a name path.
|
|
2141
|
+
* @param expression
|
|
2142
|
+
* @param mode
|
|
2143
|
+
*/
|
|
2144
|
+
function parseNamePath(expression, mode, { includeSpecial = false } = {}) {
|
|
2145
|
+
switch (mode) {
|
|
2146
|
+
case "closure": return new Parser(includeSpecial ? closureNamePathSpecialGrammar : closureNamePathGrammar, Lexer.create(looseRules, expression)).parse();
|
|
2147
|
+
case "jsdoc": return new Parser(includeSpecial ? jsdocNamePathSpecialGrammar : jsdocNamePathGrammar, Lexer.create(looseRules, expression)).parse();
|
|
2148
|
+
case "typescript": return new Parser(includeSpecial ? typescriptNamePathSpecialGrammar : typescriptNamePathGrammar, Lexer.create(rules, expression)).parse();
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
/**
|
|
2152
|
+
* This function parses the given expression in the given mode and produces a name.
|
|
2153
|
+
* @param expression
|
|
2154
|
+
* @param mode
|
|
2155
|
+
*/
|
|
2156
|
+
function parseName(expression, mode) {
|
|
2157
|
+
switch (mode) {
|
|
2158
|
+
case "closure": return new Parser(closureNameGrammar, Lexer.create(looseRules, expression)).parse();
|
|
2159
|
+
case "jsdoc": return new Parser(jsdocNameGrammar, Lexer.create(looseRules, expression)).parse();
|
|
2160
|
+
case "typescript": return new Parser(typescriptNameGrammar, Lexer.create(rules, expression)).parse();
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
//#endregion
|
|
2164
|
+
//#region src/transforms/transform.ts
|
|
2165
|
+
function transform(rules, parseResult) {
|
|
2166
|
+
const rule = rules[parseResult.type];
|
|
2167
|
+
if (rule === void 0) throw new Error(`In this set of transform rules exists no rule for type ${parseResult.type}.`);
|
|
2168
|
+
return rule(parseResult, (aParseResult) => transform(rules, aParseResult));
|
|
2169
|
+
}
|
|
2170
|
+
function notAvailableTransform(parseResult) {
|
|
2171
|
+
throw new Error("This transform is not available. Are you trying the correct parsing mode?");
|
|
2172
|
+
}
|
|
2173
|
+
function extractSpecialParams(source) {
|
|
2174
|
+
const result = { params: [] };
|
|
2175
|
+
for (const param of source.parameters) if (param.type === "JsdocTypeKeyValue") if (param.key === "this") result.this = param.right;
|
|
2176
|
+
else if (param.key === "new") result.new = param.right;
|
|
2177
|
+
else result.params.push(param);
|
|
2178
|
+
else result.params.push(param);
|
|
2179
|
+
return result;
|
|
2180
|
+
}
|
|
2181
|
+
//#endregion
|
|
2182
|
+
//#region src/transforms/stringify.ts
|
|
2183
|
+
function applyPosition(position, target, value) {
|
|
2184
|
+
return position === "prefix" ? value + target : target + value;
|
|
2185
|
+
}
|
|
2186
|
+
function quote(value, quote) {
|
|
2187
|
+
switch (quote) {
|
|
2188
|
+
case "double": return `"${value}"`;
|
|
2189
|
+
case "single": return `'${value}'`;
|
|
2190
|
+
case void 0: return value;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
function stringifyRules({ computedPropertyStringifier } = {}) {
|
|
2194
|
+
return {
|
|
2195
|
+
JsdocTypeParenthesis: (result, transform) => `(${result.element !== void 0 ? transform(result.element) : ""})`,
|
|
2196
|
+
JsdocTypeKeyof: (result, transform) => `keyof ${transform(result.element)}`,
|
|
2197
|
+
JsdocTypeFunction: (result, transform) => {
|
|
2198
|
+
if (!result.arrow) {
|
|
2199
|
+
let stringified = result.constructor ? "new" : "function";
|
|
2200
|
+
if (!result.parenthesis) return stringified;
|
|
2201
|
+
stringified += `(${result.parameters.map(transform).join(`,${result.meta?.parameterSpacing ?? " "}`)})`;
|
|
2202
|
+
if (result.returnType !== void 0) stringified += `${result.meta?.preReturnMarkerSpacing ?? ""}:${result.meta?.postReturnMarkerSpacing ?? " "}${transform(result.returnType)}`;
|
|
2203
|
+
return stringified;
|
|
2204
|
+
} else {
|
|
2205
|
+
if (result.returnType === void 0) throw new Error("Arrow function needs a return type.");
|
|
2206
|
+
let stringified = `${result.typeParameters !== void 0 ? `<${result.typeParameters.map(transform).join(`,${result.meta?.typeParameterSpacing ?? " "}`)}>${result.meta?.postGenericSpacing ?? ""}` : ""}(${result.parameters.map(transform).join(`,${result.meta?.parameterSpacing ?? " "}`)})${result.meta?.preReturnMarkerSpacing ?? " "}=>${result.meta?.postReturnMarkerSpacing ?? " "}${transform(result.returnType)}`;
|
|
2207
|
+
if (result.constructor) stringified = `new ${stringified}`;
|
|
2208
|
+
return stringified;
|
|
2209
|
+
}
|
|
2210
|
+
},
|
|
2211
|
+
JsdocTypeName: (result) => result.value,
|
|
2212
|
+
JsdocTypeInfer: (result, transform) => `infer ${transform(result.element)}`,
|
|
2213
|
+
JsdocTypeUniqueSymbol: () => "unique symbol",
|
|
2214
|
+
JsdocTypeTuple: (result, transform) => `[${result.elements.map(transform).join(`,${result.meta?.elementSpacing ?? " "}`)}]`,
|
|
2215
|
+
JsdocTypeVariadic: (result, transform) => result.meta.position === void 0 ? "..." : applyPosition(result.meta.position, transform(result.element), "..."),
|
|
2216
|
+
JsdocTypeNamePath: (result, transform) => {
|
|
2217
|
+
const left = transform(result.left);
|
|
2218
|
+
const right = transform(result.right);
|
|
2219
|
+
switch (result.pathType) {
|
|
2220
|
+
case "inner": return `${left}~${right}`;
|
|
2221
|
+
case "instance": return `${left}#${right}`;
|
|
2222
|
+
case "property": return `${left}.${right}`;
|
|
2223
|
+
case "property-brackets": return `${left}[${right}]`;
|
|
2224
|
+
}
|
|
2225
|
+
},
|
|
2226
|
+
JsdocTypeStringValue: (result) => quote(result.value, result.meta.quote),
|
|
2227
|
+
JsdocTypeAny: () => "*",
|
|
2228
|
+
JsdocTypeGeneric: (result, transform) => {
|
|
2229
|
+
if (result.meta.brackets === "square") {
|
|
2230
|
+
const element = result.elements[0];
|
|
2231
|
+
const transformed = transform(element);
|
|
2232
|
+
if (element.type === "JsdocTypeUnion" || element.type === "JsdocTypeIntersection") return `(${transformed})[]`;
|
|
2233
|
+
else return `${transformed}[]`;
|
|
2234
|
+
} else return `${transform(result.left)}${result.meta.dot ? "." : ""}<${result.elements.map(transform).join(`,${result.meta.elementSpacing ?? " "}`)}>`;
|
|
2235
|
+
},
|
|
2236
|
+
JsdocTypeImport: (result, transform) => `import(${transform(result.element)})`,
|
|
2237
|
+
JsdocTypeObjectField: (result, transform) => {
|
|
2238
|
+
let text = "";
|
|
2239
|
+
if (result.readonly) text += "readonly ";
|
|
2240
|
+
let optionalBeforeParentheses = false;
|
|
2241
|
+
if (typeof result.key === "string") text += quote(result.key, result.meta.quote);
|
|
2242
|
+
else {
|
|
2243
|
+
if (result.key.type === "JsdocTypeComputedMethod") optionalBeforeParentheses = true;
|
|
2244
|
+
text += transform(result.key);
|
|
2245
|
+
}
|
|
2246
|
+
text += result.meta.postKeySpacing ?? "";
|
|
2247
|
+
if (!optionalBeforeParentheses && result.optional) {
|
|
2248
|
+
text += "?";
|
|
2249
|
+
text += result.meta.postOptionalSpacing ?? "";
|
|
2250
|
+
}
|
|
2251
|
+
if (result.right === void 0) return text;
|
|
2252
|
+
else return `${text}:${result.meta.postColonSpacing ?? " "}${transform(result.right)}`;
|
|
2253
|
+
},
|
|
2254
|
+
JsdocTypeJsdocObjectField: (result, transform) => `${transform(result.left)}: ${transform(result.right)}`,
|
|
2255
|
+
JsdocTypeKeyValue: (result, transform) => {
|
|
2256
|
+
let text = result.key;
|
|
2257
|
+
if (result.optional) text += `${result.meta?.postKeySpacing ?? ""}?${result.meta?.postOptionalSpacing ?? ""}`;
|
|
2258
|
+
else if (result.variadic) text = `...${result.meta?.postVariadicSpacing ?? ""}${text}`;
|
|
2259
|
+
else if (result.right !== void 0) text += result.meta?.postKeySpacing ?? "";
|
|
2260
|
+
if (result.right === void 0) return text;
|
|
2261
|
+
else return `${text}:${result.meta?.postColonSpacing ?? " "}${transform(result.right)}`;
|
|
2262
|
+
},
|
|
2263
|
+
JsdocTypeSpecialNamePath: (result) => `${result.specialType}:${quote(result.value, result.meta.quote)}`,
|
|
2264
|
+
JsdocTypeNotNullable: (result, transform) => applyPosition(result.meta.position, transform(result.element), "!"),
|
|
2265
|
+
JsdocTypeNull: () => "null",
|
|
2266
|
+
JsdocTypeNullable: (result, transform) => applyPosition(result.meta.position, transform(result.element), "?"),
|
|
2267
|
+
JsdocTypeNumber: (result) => result.value.toString(),
|
|
2268
|
+
JsdocTypeBigInt: (result) => `${result.value}n`,
|
|
2269
|
+
JsdocTypeObject: (result, transform) => {
|
|
2270
|
+
/* c8 ignore next -- Guard */
|
|
2271
|
+
const lbType = (result.meta.separator ?? "").endsWith("linebreak");
|
|
2272
|
+
const lbEnding = result.meta.separator === "comma-and-linebreak" ? ",\n" : result.meta.separator === "semicolon-and-linebreak" ? ";\n" : result.meta.separator === "linebreak" ? "\n" : "";
|
|
2273
|
+
const separatorForSingleObjectField = result.meta.separatorForSingleObjectField ?? false;
|
|
2274
|
+
const trailingPunctuation = result.meta.trailingPunctuation ?? false;
|
|
2275
|
+
const bracketSpacing = result.meta.bracketSpacing ?? "";
|
|
2276
|
+
return `{${(lbType && (separatorForSingleObjectField || result.elements.length > 1) ? `\n${result.meta.propertyIndent ?? ""}` : bracketSpacing) + result.elements.map(transform).join(result.meta.separator === "comma" ? ", " : lbType ? lbEnding + (result.meta.propertyIndent ?? "") : "; ") + (separatorForSingleObjectField && result.elements.length === 1 ? result.meta.separator === "comma" ? "," : lbType ? lbEnding : ";" : trailingPunctuation && result.meta.separator !== void 0 ? result.meta.separator.startsWith("comma") ? "," : result.meta.separator.startsWith("semicolon") ? ";" : "" : "") + (lbType && result.elements.length > 1 ? "\n" : bracketSpacing)}}`;
|
|
2277
|
+
},
|
|
2278
|
+
JsdocTypeOptional: (result, transform) => applyPosition(result.meta.position, transform(result.element), "="),
|
|
2279
|
+
JsdocTypeSymbol: (result, transform) => `${result.value}(${result.element !== void 0 ? transform(result.element) : ""})`,
|
|
2280
|
+
JsdocTypeTypeof: (result, transform) => `typeof ${transform(result.element)}`,
|
|
2281
|
+
JsdocTypeUndefined: () => "undefined",
|
|
2282
|
+
JsdocTypeUnion: (result, transform) => result.elements.map(transform).join(result.meta?.spacing === void 0 ? " | " : `${result.meta.spacing}|${result.meta.spacing}`),
|
|
2283
|
+
JsdocTypeUnknown: () => "?",
|
|
2284
|
+
JsdocTypeIntersection: (result, transform) => result.elements.map(transform).join(" & "),
|
|
2285
|
+
JsdocTypeProperty: (result) => quote(result.value, result.meta.quote),
|
|
2286
|
+
JsdocTypePredicate: (result, transform) => `${transform(result.left)} is ${transform(result.right)}`,
|
|
2287
|
+
JsdocTypeIndexSignature: (result, transform) => `[${result.key}: ${transform(result.right)}]`,
|
|
2288
|
+
JsdocTypeMappedType: (result, transform) => `[${result.key} in ${transform(result.right)}]`,
|
|
2289
|
+
JsdocTypeAsserts: (result, transform) => `asserts ${transform(result.left)} is ${transform(result.right)}`,
|
|
2290
|
+
JsdocTypeReadonlyArray: (result, transform) => `readonly ${transform(result.element)}`,
|
|
2291
|
+
JsdocTypeAssertsPlain: (result, transform) => `asserts ${transform(result.element)}`,
|
|
2292
|
+
JsdocTypeConditional: (result, transform) => `${transform(result.checksType)} extends ${transform(result.extendsType)} ? ${transform(result.trueType)} : ${transform(result.falseType)}`,
|
|
2293
|
+
JsdocTypeTypeParameter: (result, transform) => `${transform(result.name)}${result.constraint !== void 0 ? ` extends ${transform(result.constraint)}` : ""}${result.defaultValue !== void 0 ? `${result.meta?.defaultValueSpacing ?? " "}=${result.meta?.defaultValueSpacing ?? " "}${transform(result.defaultValue)}` : ""}`,
|
|
2294
|
+
JsdocTypeCallSignature: (result, transform) => `${result.typeParameters !== void 0 ? `<${result.typeParameters.map(transform).join(`,${result.meta?.typeParameterSpacing ?? " "}`)}>${result.meta?.postGenericSpacing ?? ""}` : ""}(${result.parameters.map(transform).join(`,${result.meta?.parameterSpacing ?? " "}`)})${result.meta?.preReturnMarkerSpacing ?? ""}:${result.meta?.postReturnMarkerSpacing ?? " "}${transform(result.returnType)}`,
|
|
2295
|
+
JsdocTypeConstructorSignature: (result, transform) => `new${result.meta?.postNewSpacing ?? " "}${result.typeParameters !== void 0 ? `<${result.typeParameters.map(transform).join(`,${result.meta?.typeParameterSpacing ?? " "}`)}>${result.meta?.postGenericSpacing ?? ""}` : ""}(${result.parameters.map(transform).join(`,${result.meta?.parameterSpacing ?? " "}`)})${result.meta?.preReturnMarkerSpacing ?? ""}:${result.meta?.postReturnMarkerSpacing ?? " "}${transform(result.returnType)}`,
|
|
2296
|
+
JsdocTypeMethodSignature: (result, transform) => {
|
|
2297
|
+
const quote = result.meta.quote === "double" ? "\"" : result.meta.quote === "single" ? "'" : "";
|
|
2298
|
+
return `${quote}${result.name}${quote}${result.meta.postMethodNameSpacing ?? ""}${result.typeParameters !== void 0 ? `<${result.typeParameters.map(transform).join(`,${result.meta.typeParameterSpacing ?? " "}`)}>${result.meta.postGenericSpacing ?? ""}` : ""}(${result.parameters.map(transform).join(`,${result.meta.parameterSpacing ?? " "}`)})${result.meta.preReturnMarkerSpacing ?? ""}:${result.meta.postReturnMarkerSpacing ?? " "}${transform(result.returnType)}`;
|
|
2299
|
+
},
|
|
2300
|
+
JsdocTypeIndexedAccessIndex: (result, transform) => transform(result.right),
|
|
2301
|
+
JsdocTypeTemplateLiteral: (result, transform) => `\`${result.literals.slice(0, -1).map((literal, idx) => `${literal.replace(/`/gv, "\\`")}\${${transform(result.interpolations[idx])}}`).join("") + result.literals.slice(-1)[0].replace(/`/gv, "\\`")}\``,
|
|
2302
|
+
JsdocTypeComputedProperty: (result, transform) => {
|
|
2303
|
+
if (result.value.type.startsWith("JsdocType")) return `[${transform(result.value)}]`;
|
|
2304
|
+
else {
|
|
2305
|
+
if (computedPropertyStringifier === void 0) throw new Error("Must have a computed property stringifier");
|
|
2306
|
+
return `[${computedPropertyStringifier(result.value).replace(/;$/v, "")}]`;
|
|
2307
|
+
}
|
|
2308
|
+
},
|
|
2309
|
+
JsdocTypeComputedMethod: (result, transform) => {
|
|
2310
|
+
if (result.value.type.startsWith("JsdocType")) return `[${transform(result.value)}]${result.optional ? "?" : ""}${result.typeParameters !== void 0 ? `<${result.typeParameters.map(transform).join(`,${result.meta?.typeParameterSpacing ?? " "}`)}>${result.meta?.postGenericSpacing ?? ""}` : ""}(${result.parameters.map(transform).join(`,${result.meta?.parameterSpacing ?? " "}`)})${result.meta?.preReturnMarkerSpacing ?? ""}:${result.meta?.postReturnMarkerSpacing ?? " "}${transform(result.returnType)}`;
|
|
2311
|
+
else {
|
|
2312
|
+
if (computedPropertyStringifier === void 0) throw new Error("Must have a computed property stringifier");
|
|
2313
|
+
return `[${computedPropertyStringifier(result.value).replace(/;$/v, "")}](${result.parameters.map(transform).join(`,${result.meta?.parameterSpacing ?? " "}`)})${result.meta?.preReturnMarkerSpacing ?? ""}:${result.meta?.postReturnMarkerSpacing ?? " "}${transform(result.returnType)}`;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
const storedStringifyRules = stringifyRules();
|
|
2319
|
+
function stringify(result, stringificationRules = storedStringifyRules) {
|
|
2320
|
+
if (typeof stringificationRules === "function") stringificationRules = stringifyRules({ computedPropertyStringifier: stringificationRules });
|
|
2321
|
+
return transform(stringificationRules, result);
|
|
2322
|
+
}
|
|
2323
|
+
//#endregion
|
|
2324
|
+
//#region src/transforms/catharsisTransform.ts
|
|
2325
|
+
const reservedWords = [
|
|
2326
|
+
"null",
|
|
2327
|
+
"true",
|
|
2328
|
+
"false",
|
|
2329
|
+
"break",
|
|
2330
|
+
"case",
|
|
2331
|
+
"catch",
|
|
2332
|
+
"class",
|
|
2333
|
+
"const",
|
|
2334
|
+
"continue",
|
|
2335
|
+
"debugger",
|
|
2336
|
+
"default",
|
|
2337
|
+
"delete",
|
|
2338
|
+
"do",
|
|
2339
|
+
"else",
|
|
2340
|
+
"export",
|
|
2341
|
+
"extends",
|
|
2342
|
+
"finally",
|
|
2343
|
+
"for",
|
|
2344
|
+
"function",
|
|
2345
|
+
"if",
|
|
2346
|
+
"import",
|
|
2347
|
+
"in",
|
|
2348
|
+
"instanceof",
|
|
2349
|
+
"new",
|
|
2350
|
+
"return",
|
|
2351
|
+
"super",
|
|
2352
|
+
"switch",
|
|
2353
|
+
"this",
|
|
2354
|
+
"throw",
|
|
2355
|
+
"try",
|
|
2356
|
+
"typeof",
|
|
2357
|
+
"var",
|
|
2358
|
+
"void",
|
|
2359
|
+
"while",
|
|
2360
|
+
"with",
|
|
2361
|
+
"yield"
|
|
2362
|
+
];
|
|
2363
|
+
function makeName(value) {
|
|
2364
|
+
const result = {
|
|
2365
|
+
type: "NameExpression",
|
|
2366
|
+
name: value
|
|
2367
|
+
};
|
|
2368
|
+
if (reservedWords.includes(value)) result.reservedWord = true;
|
|
2369
|
+
return result;
|
|
2370
|
+
}
|
|
2371
|
+
const catharsisTransformRules = {
|
|
2372
|
+
JsdocTypeOptional: (result, transform) => {
|
|
2373
|
+
const transformed = transform(result.element);
|
|
2374
|
+
transformed.optional = true;
|
|
2375
|
+
return transformed;
|
|
2376
|
+
},
|
|
2377
|
+
JsdocTypeNullable: (result, transform) => {
|
|
2378
|
+
const transformed = transform(result.element);
|
|
2379
|
+
transformed.nullable = true;
|
|
2380
|
+
return transformed;
|
|
2381
|
+
},
|
|
2382
|
+
JsdocTypeNotNullable: (result, transform) => {
|
|
2383
|
+
const transformed = transform(result.element);
|
|
2384
|
+
transformed.nullable = false;
|
|
2385
|
+
return transformed;
|
|
2386
|
+
},
|
|
2387
|
+
JsdocTypeVariadic: (result, transform) => {
|
|
2388
|
+
if (result.element === void 0) throw new Error("dots without value are not allowed in catharsis mode");
|
|
2389
|
+
const transformed = transform(result.element);
|
|
2390
|
+
transformed.repeatable = true;
|
|
2391
|
+
return transformed;
|
|
2392
|
+
},
|
|
2393
|
+
JsdocTypeAny: () => ({ type: "AllLiteral" }),
|
|
2394
|
+
JsdocTypeNull: () => ({ type: "NullLiteral" }),
|
|
2395
|
+
JsdocTypeStringValue: (result) => makeName(quote(result.value, result.meta.quote)),
|
|
2396
|
+
JsdocTypeUndefined: () => ({ type: "UndefinedLiteral" }),
|
|
2397
|
+
JsdocTypeUnknown: () => ({ type: "UnknownLiteral" }),
|
|
2398
|
+
JsdocTypeFunction: (result, transform) => {
|
|
2399
|
+
const params = extractSpecialParams(result);
|
|
2400
|
+
const transformed = {
|
|
2401
|
+
type: "FunctionType",
|
|
2402
|
+
params: params.params.map(transform)
|
|
2403
|
+
};
|
|
2404
|
+
if (params.this !== void 0) transformed.this = transform(params.this);
|
|
2405
|
+
if (params.new !== void 0) transformed.new = transform(params.new);
|
|
2406
|
+
if (result.returnType !== void 0) transformed.result = transform(result.returnType);
|
|
2407
|
+
return transformed;
|
|
2408
|
+
},
|
|
2409
|
+
JsdocTypeGeneric: (result, transform) => ({
|
|
2410
|
+
type: "TypeApplication",
|
|
2411
|
+
applications: result.elements.map((o) => transform(o)),
|
|
2412
|
+
expression: transform(result.left)
|
|
2413
|
+
}),
|
|
2414
|
+
JsdocTypeSpecialNamePath: (result) => makeName(`${result.specialType}:${quote(result.value, result.meta.quote)}`),
|
|
2415
|
+
JsdocTypeName: (result) => {
|
|
2416
|
+
if (result.value !== "function") return makeName(result.value);
|
|
2417
|
+
else return {
|
|
2418
|
+
type: "FunctionType",
|
|
2419
|
+
params: []
|
|
2420
|
+
};
|
|
2421
|
+
},
|
|
2422
|
+
JsdocTypeNumber: (result) => makeName(result.value.toString()),
|
|
2423
|
+
JsdocTypeBigInt: (result) => makeName(`${result.value}n`),
|
|
2424
|
+
JsdocTypeObject: (result, transform) => {
|
|
2425
|
+
const transformed = {
|
|
2426
|
+
type: "RecordType",
|
|
2427
|
+
fields: []
|
|
2428
|
+
};
|
|
2429
|
+
for (const field of result.elements) if (field.type !== "JsdocTypeObjectField" && field.type !== "JsdocTypeJsdocObjectField") transformed.fields.push({
|
|
2430
|
+
type: "FieldType",
|
|
2431
|
+
key: transform(field),
|
|
2432
|
+
value: void 0
|
|
2433
|
+
});
|
|
2434
|
+
else transformed.fields.push(transform(field));
|
|
2435
|
+
return transformed;
|
|
2436
|
+
},
|
|
2437
|
+
JsdocTypeObjectField: (result, transform) => {
|
|
2438
|
+
if (typeof result.key !== "string") throw new Error("Index signatures and mapped types are not supported");
|
|
2439
|
+
return {
|
|
2440
|
+
type: "FieldType",
|
|
2441
|
+
key: makeName(quote(result.key, result.meta.quote)),
|
|
2442
|
+
value: result.right === void 0 ? void 0 : transform(result.right)
|
|
2443
|
+
};
|
|
2444
|
+
},
|
|
2445
|
+
JsdocTypeJsdocObjectField: (result, transform) => ({
|
|
2446
|
+
type: "FieldType",
|
|
2447
|
+
key: transform(result.left),
|
|
2448
|
+
value: transform(result.right)
|
|
2449
|
+
}),
|
|
2450
|
+
JsdocTypeUnion: (result, transform) => ({
|
|
2451
|
+
type: "TypeUnion",
|
|
2452
|
+
elements: result.elements.map((e) => transform(e))
|
|
2453
|
+
}),
|
|
2454
|
+
JsdocTypeKeyValue: (result, transform) => ({
|
|
2455
|
+
type: "FieldType",
|
|
2456
|
+
key: makeName(result.key),
|
|
2457
|
+
value: result.right === void 0 ? void 0 : transform(result.right)
|
|
2458
|
+
}),
|
|
2459
|
+
JsdocTypeNamePath: (result, transform) => {
|
|
2460
|
+
const leftResult = transform(result.left);
|
|
2461
|
+
let rightValue;
|
|
2462
|
+
if (result.right.type === "JsdocTypeIndexedAccessIndex") throw new TypeError("JsdocTypeIndexedAccessIndex is not supported in catharsis");
|
|
2463
|
+
if (result.right.type === "JsdocTypeSpecialNamePath") rightValue = transform(result.right).name;
|
|
2464
|
+
else rightValue = quote(result.right.value, result.right.meta.quote);
|
|
2465
|
+
const joiner = result.pathType === "inner" ? "~" : result.pathType === "instance" ? "#" : ".";
|
|
2466
|
+
return makeName(`${leftResult.name}${joiner}${rightValue}`);
|
|
2467
|
+
},
|
|
2468
|
+
JsdocTypeSymbol: (result) => {
|
|
2469
|
+
let value = "";
|
|
2470
|
+
let element = result.element;
|
|
2471
|
+
let trailingDots = false;
|
|
2472
|
+
if (element?.type === "JsdocTypeVariadic") {
|
|
2473
|
+
if (element.meta.position === "prefix") value = "...";
|
|
2474
|
+
else trailingDots = true;
|
|
2475
|
+
element = element.element;
|
|
2476
|
+
}
|
|
2477
|
+
if (element?.type === "JsdocTypeName") value += element.value;
|
|
2478
|
+
else if (element?.type === "JsdocTypeNumber") value += element.value.toString();
|
|
2479
|
+
if (trailingDots) value += "...";
|
|
2480
|
+
return makeName(`${result.value}(${value})`);
|
|
2481
|
+
},
|
|
2482
|
+
JsdocTypeParenthesis: (result, transform) => transform(assertRootResult(result.element)),
|
|
2483
|
+
JsdocTypeMappedType: notAvailableTransform,
|
|
2484
|
+
JsdocTypeIndexSignature: notAvailableTransform,
|
|
2485
|
+
JsdocTypeInfer: notAvailableTransform,
|
|
2486
|
+
JsdocTypeUniqueSymbol: notAvailableTransform,
|
|
2487
|
+
JsdocTypeImport: notAvailableTransform,
|
|
2488
|
+
JsdocTypeKeyof: notAvailableTransform,
|
|
2489
|
+
JsdocTypeTuple: notAvailableTransform,
|
|
2490
|
+
JsdocTypeTypeof: notAvailableTransform,
|
|
2491
|
+
JsdocTypeIntersection: notAvailableTransform,
|
|
2492
|
+
JsdocTypeProperty: notAvailableTransform,
|
|
2493
|
+
JsdocTypePredicate: notAvailableTransform,
|
|
2494
|
+
JsdocTypeAsserts: notAvailableTransform,
|
|
2495
|
+
JsdocTypeReadonlyArray: notAvailableTransform,
|
|
2496
|
+
JsdocTypeAssertsPlain: notAvailableTransform,
|
|
2497
|
+
JsdocTypeConditional: notAvailableTransform,
|
|
2498
|
+
JsdocTypeTypeParameter: notAvailableTransform,
|
|
2499
|
+
JsdocTypeCallSignature: notAvailableTransform,
|
|
2500
|
+
JsdocTypeConstructorSignature: notAvailableTransform,
|
|
2501
|
+
JsdocTypeMethodSignature: notAvailableTransform,
|
|
2502
|
+
JsdocTypeIndexedAccessIndex: notAvailableTransform,
|
|
2503
|
+
JsdocTypeTemplateLiteral: notAvailableTransform,
|
|
2504
|
+
JsdocTypeComputedProperty: notAvailableTransform,
|
|
2505
|
+
JsdocTypeComputedMethod: notAvailableTransform
|
|
2506
|
+
};
|
|
2507
|
+
function catharsisTransform(result) {
|
|
2508
|
+
return transform(catharsisTransformRules, result);
|
|
2509
|
+
}
|
|
2510
|
+
//#endregion
|
|
2511
|
+
//#region src/transforms/jtpTransform.ts
|
|
2512
|
+
function getQuoteStyle(quote) {
|
|
2513
|
+
switch (quote) {
|
|
2514
|
+
case void 0: return "none";
|
|
2515
|
+
case "single": return "single";
|
|
2516
|
+
case "double": return "double";
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
function getMemberType(type) {
|
|
2520
|
+
switch (type) {
|
|
2521
|
+
case "inner": return "INNER_MEMBER";
|
|
2522
|
+
case "instance": return "INSTANCE_MEMBER";
|
|
2523
|
+
case "property": return "MEMBER";
|
|
2524
|
+
case "property-brackets": return "MEMBER";
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
function nestResults(type, results) {
|
|
2528
|
+
if (results.length === 2) return {
|
|
2529
|
+
type,
|
|
2530
|
+
left: results[0],
|
|
2531
|
+
right: results[1]
|
|
2532
|
+
};
|
|
2533
|
+
else return {
|
|
2534
|
+
type,
|
|
2535
|
+
left: results[0],
|
|
2536
|
+
right: nestResults(type, results.slice(1))
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
const jtpRules = {
|
|
2540
|
+
JsdocTypeOptional: (result, transform) => ({
|
|
2541
|
+
type: "OPTIONAL",
|
|
2542
|
+
value: transform(result.element),
|
|
2543
|
+
meta: { syntax: result.meta.position === "prefix" ? "PREFIX_EQUAL_SIGN" : "SUFFIX_EQUALS_SIGN" }
|
|
2544
|
+
}),
|
|
2545
|
+
JsdocTypeNullable: (result, transform) => ({
|
|
2546
|
+
type: "NULLABLE",
|
|
2547
|
+
value: transform(result.element),
|
|
2548
|
+
meta: { syntax: result.meta.position === "prefix" ? "PREFIX_QUESTION_MARK" : "SUFFIX_QUESTION_MARK" }
|
|
2549
|
+
}),
|
|
2550
|
+
JsdocTypeNotNullable: (result, transform) => ({
|
|
2551
|
+
type: "NOT_NULLABLE",
|
|
2552
|
+
value: transform(result.element),
|
|
2553
|
+
meta: { syntax: result.meta.position === "prefix" ? "PREFIX_BANG" : "SUFFIX_BANG" }
|
|
2554
|
+
}),
|
|
2555
|
+
JsdocTypeVariadic: (result, transform) => {
|
|
2556
|
+
const transformed = {
|
|
2557
|
+
type: "VARIADIC",
|
|
2558
|
+
meta: { syntax: result.meta.position === "prefix" ? "PREFIX_DOTS" : result.meta.position === "suffix" ? "SUFFIX_DOTS" : "ONLY_DOTS" }
|
|
2559
|
+
};
|
|
2560
|
+
if (result.element !== void 0) transformed.value = transform(result.element);
|
|
2561
|
+
return transformed;
|
|
2562
|
+
},
|
|
2563
|
+
JsdocTypeName: (result) => ({
|
|
2564
|
+
type: "NAME",
|
|
2565
|
+
name: result.value
|
|
2566
|
+
}),
|
|
2567
|
+
JsdocTypeTypeof: (result, transform) => ({
|
|
2568
|
+
type: "TYPE_QUERY",
|
|
2569
|
+
name: transform(result.element)
|
|
2570
|
+
}),
|
|
2571
|
+
JsdocTypeTuple: (result, transform) => ({
|
|
2572
|
+
type: "TUPLE",
|
|
2573
|
+
entries: result.elements.map(transform)
|
|
2574
|
+
}),
|
|
2575
|
+
JsdocTypeKeyof: (result, transform) => ({
|
|
2576
|
+
type: "KEY_QUERY",
|
|
2577
|
+
value: transform(result.element)
|
|
2578
|
+
}),
|
|
2579
|
+
JsdocTypeImport: (result) => ({
|
|
2580
|
+
type: "IMPORT",
|
|
2581
|
+
path: {
|
|
2582
|
+
type: "STRING_VALUE",
|
|
2583
|
+
quoteStyle: getQuoteStyle(result.element.meta.quote),
|
|
2584
|
+
string: result.element.value
|
|
2585
|
+
}
|
|
2586
|
+
}),
|
|
2587
|
+
JsdocTypeUndefined: () => ({
|
|
2588
|
+
type: "NAME",
|
|
2589
|
+
name: "undefined"
|
|
2590
|
+
}),
|
|
2591
|
+
JsdocTypeAny: () => ({ type: "ANY" }),
|
|
2592
|
+
JsdocTypeFunction: (result, transform) => {
|
|
2593
|
+
const specialParams = extractSpecialParams(result);
|
|
2594
|
+
const transformed = {
|
|
2595
|
+
type: result.arrow ? "ARROW" : "FUNCTION",
|
|
2596
|
+
params: specialParams.params.map((param) => {
|
|
2597
|
+
if (param.type === "JsdocTypeKeyValue") {
|
|
2598
|
+
if (param.right === void 0) throw new Error("Function parameter without ':' is not expected to be 'KEY_VALUE'");
|
|
2599
|
+
return {
|
|
2600
|
+
type: "NAMED_PARAMETER",
|
|
2601
|
+
name: param.key,
|
|
2602
|
+
typeName: transform(param.right)
|
|
2603
|
+
};
|
|
2604
|
+
} else return transform(param);
|
|
2605
|
+
}),
|
|
2606
|
+
new: null,
|
|
2607
|
+
returns: null
|
|
2608
|
+
};
|
|
2609
|
+
if (specialParams.this !== void 0) transformed.this = transform(specialParams.this);
|
|
2610
|
+
else if (!result.arrow) transformed.this = null;
|
|
2611
|
+
if (specialParams.new !== void 0) transformed.new = transform(specialParams.new);
|
|
2612
|
+
if (result.returnType !== void 0) transformed.returns = transform(result.returnType);
|
|
2613
|
+
return transformed;
|
|
2614
|
+
},
|
|
2615
|
+
JsdocTypeGeneric: (result, transform) => {
|
|
2616
|
+
const transformed = {
|
|
2617
|
+
type: "GENERIC",
|
|
2618
|
+
subject: transform(result.left),
|
|
2619
|
+
objects: result.elements.map(transform),
|
|
2620
|
+
meta: { syntax: result.meta.brackets === "square" ? "SQUARE_BRACKET" : result.meta.dot ? "ANGLE_BRACKET_WITH_DOT" : "ANGLE_BRACKET" }
|
|
2621
|
+
};
|
|
2622
|
+
if (result.meta.brackets === "square" && result.elements[0].type === "JsdocTypeFunction" && !result.elements[0].parenthesis) transformed.objects[0] = {
|
|
2623
|
+
type: "NAME",
|
|
2624
|
+
name: "function"
|
|
2625
|
+
};
|
|
2626
|
+
return transformed;
|
|
2627
|
+
},
|
|
2628
|
+
JsdocTypeObjectField: (result, transform) => {
|
|
2629
|
+
if (typeof result.key !== "string") throw new Error("Index signatures and mapped types are not supported");
|
|
2630
|
+
if (result.right === void 0) return {
|
|
2631
|
+
type: "RECORD_ENTRY",
|
|
2632
|
+
key: result.key,
|
|
2633
|
+
quoteStyle: getQuoteStyle(result.meta.quote),
|
|
2634
|
+
value: null,
|
|
2635
|
+
readonly: false
|
|
2636
|
+
};
|
|
2637
|
+
let right = transform(result.right);
|
|
2638
|
+
if (result.optional) right = {
|
|
2639
|
+
type: "OPTIONAL",
|
|
2640
|
+
value: right,
|
|
2641
|
+
meta: { syntax: "SUFFIX_KEY_QUESTION_MARK" }
|
|
2642
|
+
};
|
|
2643
|
+
return {
|
|
2644
|
+
type: "RECORD_ENTRY",
|
|
2645
|
+
key: result.key,
|
|
2646
|
+
quoteStyle: getQuoteStyle(result.meta.quote),
|
|
2647
|
+
value: right,
|
|
2648
|
+
readonly: false
|
|
2649
|
+
};
|
|
2650
|
+
},
|
|
2651
|
+
JsdocTypeJsdocObjectField: () => {
|
|
2652
|
+
throw new Error("Keys may not be typed in jsdoctypeparser.");
|
|
2653
|
+
},
|
|
2654
|
+
JsdocTypeKeyValue: (result, transform) => {
|
|
2655
|
+
if (result.right === void 0) return {
|
|
2656
|
+
type: "RECORD_ENTRY",
|
|
2657
|
+
key: result.key,
|
|
2658
|
+
quoteStyle: "none",
|
|
2659
|
+
value: null,
|
|
2660
|
+
readonly: false
|
|
2661
|
+
};
|
|
2662
|
+
let right = transform(result.right);
|
|
2663
|
+
if (result.optional) right = {
|
|
2664
|
+
type: "OPTIONAL",
|
|
2665
|
+
value: right,
|
|
2666
|
+
meta: { syntax: "SUFFIX_KEY_QUESTION_MARK" }
|
|
2667
|
+
};
|
|
2668
|
+
return {
|
|
2669
|
+
type: "RECORD_ENTRY",
|
|
2670
|
+
key: result.key,
|
|
2671
|
+
quoteStyle: "none",
|
|
2672
|
+
value: right,
|
|
2673
|
+
readonly: false
|
|
2674
|
+
};
|
|
2675
|
+
},
|
|
2676
|
+
JsdocTypeObject: (result, transform) => {
|
|
2677
|
+
const entries = [];
|
|
2678
|
+
for (const field of result.elements) if (field.type === "JsdocTypeObjectField" || field.type === "JsdocTypeJsdocObjectField") entries.push(transform(field));
|
|
2679
|
+
return {
|
|
2680
|
+
type: "RECORD",
|
|
2681
|
+
entries
|
|
2682
|
+
};
|
|
2683
|
+
},
|
|
2684
|
+
JsdocTypeSpecialNamePath: (result) => {
|
|
2685
|
+
if (result.specialType !== "module") throw new Error(`jsdoctypeparser does not support type ${result.specialType} at this point.`);
|
|
2686
|
+
return {
|
|
2687
|
+
type: "MODULE",
|
|
2688
|
+
value: {
|
|
2689
|
+
type: "FILE_PATH",
|
|
2690
|
+
quoteStyle: getQuoteStyle(result.meta.quote),
|
|
2691
|
+
path: result.value
|
|
2692
|
+
}
|
|
2693
|
+
};
|
|
2694
|
+
},
|
|
2695
|
+
JsdocTypeNamePath: (result, transform) => {
|
|
2696
|
+
let hasEventPrefix = false;
|
|
2697
|
+
let name;
|
|
2698
|
+
let quoteStyle;
|
|
2699
|
+
if (result.right.type === "JsdocTypeIndexedAccessIndex") throw new TypeError("JsdocTypeIndexedAccessIndex not allowed in jtp");
|
|
2700
|
+
if (result.right.type === "JsdocTypeSpecialNamePath" && result.right.specialType === "event") {
|
|
2701
|
+
hasEventPrefix = true;
|
|
2702
|
+
name = result.right.value;
|
|
2703
|
+
quoteStyle = getQuoteStyle(result.right.meta.quote);
|
|
2704
|
+
} else {
|
|
2705
|
+
name = result.right.value;
|
|
2706
|
+
quoteStyle = getQuoteStyle(result.right.meta.quote);
|
|
2707
|
+
}
|
|
2708
|
+
const transformed = {
|
|
2709
|
+
type: getMemberType(result.pathType),
|
|
2710
|
+
owner: transform(result.left),
|
|
2711
|
+
name,
|
|
2712
|
+
quoteStyle,
|
|
2713
|
+
hasEventPrefix
|
|
2714
|
+
};
|
|
2715
|
+
if (transformed.owner.type === "MODULE") {
|
|
2716
|
+
const tModule = transformed.owner;
|
|
2717
|
+
transformed.owner = transformed.owner.value;
|
|
2718
|
+
tModule.value = transformed;
|
|
2719
|
+
return tModule;
|
|
2720
|
+
} else return transformed;
|
|
2721
|
+
},
|
|
2722
|
+
JsdocTypeUnion: (result, transform) => nestResults("UNION", result.elements.map(transform)),
|
|
2723
|
+
JsdocTypeParenthesis: (result, transform) => ({
|
|
2724
|
+
type: "PARENTHESIS",
|
|
2725
|
+
value: transform(assertRootResult(result.element))
|
|
2726
|
+
}),
|
|
2727
|
+
JsdocTypeNull: () => ({
|
|
2728
|
+
type: "NAME",
|
|
2729
|
+
name: "null"
|
|
2730
|
+
}),
|
|
2731
|
+
JsdocTypeUnknown: () => ({ type: "UNKNOWN" }),
|
|
2732
|
+
JsdocTypeStringValue: (result) => ({
|
|
2733
|
+
type: "STRING_VALUE",
|
|
2734
|
+
quoteStyle: getQuoteStyle(result.meta.quote),
|
|
2735
|
+
string: result.value
|
|
2736
|
+
}),
|
|
2737
|
+
JsdocTypeIntersection: (result, transform) => nestResults("INTERSECTION", result.elements.map(transform)),
|
|
2738
|
+
JsdocTypeNumber: (result) => ({
|
|
2739
|
+
type: "NUMBER_VALUE",
|
|
2740
|
+
number: result.value.toString()
|
|
2741
|
+
}),
|
|
2742
|
+
JsdocTypeBigInt: (result) => ({
|
|
2743
|
+
type: "NUMBER_VALUE",
|
|
2744
|
+
number: `${result.value}n`
|
|
2745
|
+
}),
|
|
2746
|
+
JsdocTypeSymbol: notAvailableTransform,
|
|
2747
|
+
JsdocTypeProperty: notAvailableTransform,
|
|
2748
|
+
JsdocTypePredicate: notAvailableTransform,
|
|
2749
|
+
JsdocTypeMappedType: notAvailableTransform,
|
|
2750
|
+
JsdocTypeIndexSignature: notAvailableTransform,
|
|
2751
|
+
JsdocTypeInfer: notAvailableTransform,
|
|
2752
|
+
JsdocTypeUniqueSymbol: notAvailableTransform,
|
|
2753
|
+
JsdocTypeAsserts: notAvailableTransform,
|
|
2754
|
+
JsdocTypeReadonlyArray: notAvailableTransform,
|
|
2755
|
+
JsdocTypeAssertsPlain: notAvailableTransform,
|
|
2756
|
+
JsdocTypeConditional: notAvailableTransform,
|
|
2757
|
+
JsdocTypeTypeParameter: notAvailableTransform,
|
|
2758
|
+
JsdocTypeCallSignature: notAvailableTransform,
|
|
2759
|
+
JsdocTypeConstructorSignature: notAvailableTransform,
|
|
2760
|
+
JsdocTypeMethodSignature: notAvailableTransform,
|
|
2761
|
+
JsdocTypeIndexedAccessIndex: notAvailableTransform,
|
|
2762
|
+
JsdocTypeTemplateLiteral: notAvailableTransform,
|
|
2763
|
+
JsdocTypeComputedProperty: notAvailableTransform,
|
|
2764
|
+
JsdocTypeComputedMethod: notAvailableTransform
|
|
2765
|
+
};
|
|
2766
|
+
function jtpTransform(result) {
|
|
2767
|
+
return transform(jtpRules, result);
|
|
2768
|
+
}
|
|
2769
|
+
//#endregion
|
|
2770
|
+
//#region src/transforms/identityTransformRules.ts
|
|
2771
|
+
function identityTransformRules() {
|
|
2772
|
+
return {
|
|
2773
|
+
JsdocTypeIntersection: (result, transform) => ({
|
|
2774
|
+
type: "JsdocTypeIntersection",
|
|
2775
|
+
elements: result.elements.map(transform)
|
|
2776
|
+
}),
|
|
2777
|
+
JsdocTypeGeneric: (result, transform) => ({
|
|
2778
|
+
type: "JsdocTypeGeneric",
|
|
2779
|
+
left: transform(result.left),
|
|
2780
|
+
elements: result.elements.map(transform),
|
|
2781
|
+
meta: {
|
|
2782
|
+
dot: result.meta.dot,
|
|
2783
|
+
brackets: result.meta.brackets
|
|
2784
|
+
}
|
|
2785
|
+
}),
|
|
2786
|
+
JsdocTypeNullable: (result) => result,
|
|
2787
|
+
JsdocTypeUnion: (result, transform) => ({
|
|
2788
|
+
type: "JsdocTypeUnion",
|
|
2789
|
+
elements: result.elements.map(transform)
|
|
2790
|
+
}),
|
|
2791
|
+
JsdocTypeUnknown: (result) => result,
|
|
2792
|
+
JsdocTypeUndefined: (result) => result,
|
|
2793
|
+
JsdocTypeTypeof: (result, transform) => ({
|
|
2794
|
+
type: "JsdocTypeTypeof",
|
|
2795
|
+
element: transform(result.element)
|
|
2796
|
+
}),
|
|
2797
|
+
JsdocTypeSymbol: (result, transform) => {
|
|
2798
|
+
const transformed = {
|
|
2799
|
+
type: "JsdocTypeSymbol",
|
|
2800
|
+
value: result.value
|
|
2801
|
+
};
|
|
2802
|
+
if (result.element !== void 0) transformed.element = transform(result.element);
|
|
2803
|
+
return transformed;
|
|
2804
|
+
},
|
|
2805
|
+
JsdocTypeOptional: (result, transform) => ({
|
|
2806
|
+
type: "JsdocTypeOptional",
|
|
2807
|
+
element: transform(result.element),
|
|
2808
|
+
meta: { position: result.meta.position }
|
|
2809
|
+
}),
|
|
2810
|
+
JsdocTypeObject: (result, transform) => ({
|
|
2811
|
+
type: "JsdocTypeObject",
|
|
2812
|
+
meta: { separator: "comma" },
|
|
2813
|
+
elements: result.elements.map(transform)
|
|
2814
|
+
}),
|
|
2815
|
+
JsdocTypeNumber: (result) => result,
|
|
2816
|
+
JsdocTypeBigInt: (result) => result,
|
|
2817
|
+
JsdocTypeNull: (result) => result,
|
|
2818
|
+
JsdocTypeNotNullable: (result, transform) => ({
|
|
2819
|
+
type: "JsdocTypeNotNullable",
|
|
2820
|
+
element: transform(result.element),
|
|
2821
|
+
meta: { position: result.meta.position }
|
|
2822
|
+
}),
|
|
2823
|
+
JsdocTypeSpecialNamePath: (result) => result,
|
|
2824
|
+
JsdocTypeObjectField: (result, transform) => ({
|
|
2825
|
+
type: "JsdocTypeObjectField",
|
|
2826
|
+
key: result.key,
|
|
2827
|
+
right: result.right === void 0 ? void 0 : transform(result.right),
|
|
2828
|
+
optional: result.optional,
|
|
2829
|
+
readonly: result.readonly,
|
|
2830
|
+
meta: result.meta
|
|
2831
|
+
}),
|
|
2832
|
+
JsdocTypeJsdocObjectField: (result, transform) => ({
|
|
2833
|
+
type: "JsdocTypeJsdocObjectField",
|
|
2834
|
+
left: transform(result.left),
|
|
2835
|
+
right: transform(result.right)
|
|
2836
|
+
}),
|
|
2837
|
+
JsdocTypeKeyValue: (result, transform) => ({
|
|
2838
|
+
type: "JsdocTypeKeyValue",
|
|
2839
|
+
key: result.key,
|
|
2840
|
+
right: result.right === void 0 ? void 0 : transform(result.right),
|
|
2841
|
+
optional: result.optional,
|
|
2842
|
+
variadic: result.variadic
|
|
2843
|
+
}),
|
|
2844
|
+
JsdocTypeImport: (result, transform) => ({
|
|
2845
|
+
type: "JsdocTypeImport",
|
|
2846
|
+
element: transform(result.element)
|
|
2847
|
+
}),
|
|
2848
|
+
JsdocTypeAny: (result) => result,
|
|
2849
|
+
JsdocTypeStringValue: (result) => result,
|
|
2850
|
+
JsdocTypeNamePath: (result) => result,
|
|
2851
|
+
JsdocTypeVariadic: (result, transform) => {
|
|
2852
|
+
const transformed = {
|
|
2853
|
+
type: "JsdocTypeVariadic",
|
|
2854
|
+
meta: {
|
|
2855
|
+
position: result.meta.position,
|
|
2856
|
+
squareBrackets: result.meta.squareBrackets
|
|
2857
|
+
}
|
|
2858
|
+
};
|
|
2859
|
+
if (result.element !== void 0) transformed.element = transform(result.element);
|
|
2860
|
+
return transformed;
|
|
2861
|
+
},
|
|
2862
|
+
JsdocTypeTuple: (result, transform) => ({
|
|
2863
|
+
type: "JsdocTypeTuple",
|
|
2864
|
+
elements: result.elements.map(transform)
|
|
2865
|
+
}),
|
|
2866
|
+
JsdocTypeName: (result) => result,
|
|
2867
|
+
JsdocTypeInfer: (result, transform) => ({
|
|
2868
|
+
type: "JsdocTypeInfer",
|
|
2869
|
+
element: transform(result.element)
|
|
2870
|
+
}),
|
|
2871
|
+
JsdocTypeUniqueSymbol: (result) => result,
|
|
2872
|
+
JsdocTypeFunction: (result, transform) => {
|
|
2873
|
+
const transformed = {
|
|
2874
|
+
type: "JsdocTypeFunction",
|
|
2875
|
+
arrow: result.arrow,
|
|
2876
|
+
parameters: result.parameters.map(transform),
|
|
2877
|
+
constructor: result.constructor,
|
|
2878
|
+
parenthesis: result.parenthesis
|
|
2879
|
+
};
|
|
2880
|
+
if (result.returnType !== void 0) transformed.returnType = transform(result.returnType);
|
|
2881
|
+
return transformed;
|
|
2882
|
+
},
|
|
2883
|
+
JsdocTypeKeyof: (result, transform) => ({
|
|
2884
|
+
type: "JsdocTypeKeyof",
|
|
2885
|
+
element: transform(result.element)
|
|
2886
|
+
}),
|
|
2887
|
+
JsdocTypeParenthesis: (result, transform) => ({
|
|
2888
|
+
type: "JsdocTypeParenthesis",
|
|
2889
|
+
element: transform(result.element)
|
|
2890
|
+
}),
|
|
2891
|
+
JsdocTypeProperty: (result) => result,
|
|
2892
|
+
JsdocTypePredicate: (result, transform) => ({
|
|
2893
|
+
type: "JsdocTypePredicate",
|
|
2894
|
+
left: transform(result.left),
|
|
2895
|
+
right: transform(result.right)
|
|
2896
|
+
}),
|
|
2897
|
+
JsdocTypeIndexSignature: (result, transform) => ({
|
|
2898
|
+
type: "JsdocTypeIndexSignature",
|
|
2899
|
+
key: result.key,
|
|
2900
|
+
right: transform(result.right)
|
|
2901
|
+
}),
|
|
2902
|
+
JsdocTypeMappedType: (result, transform) => ({
|
|
2903
|
+
type: "JsdocTypeMappedType",
|
|
2904
|
+
key: result.key,
|
|
2905
|
+
right: transform(result.right)
|
|
2906
|
+
}),
|
|
2907
|
+
JsdocTypeAsserts: (result, transform) => ({
|
|
2908
|
+
type: "JsdocTypeAsserts",
|
|
2909
|
+
left: transform(result.left),
|
|
2910
|
+
right: transform(result.right)
|
|
2911
|
+
}),
|
|
2912
|
+
JsdocTypeReadonlyArray: (result, transform) => ({
|
|
2913
|
+
type: "JsdocTypeReadonlyArray",
|
|
2914
|
+
element: transform(result.element)
|
|
2915
|
+
}),
|
|
2916
|
+
JsdocTypeAssertsPlain: (result, transform) => ({
|
|
2917
|
+
type: "JsdocTypeAssertsPlain",
|
|
2918
|
+
element: transform(result.element)
|
|
2919
|
+
}),
|
|
2920
|
+
JsdocTypeConditional: (result, transform) => ({
|
|
2921
|
+
type: "JsdocTypeConditional",
|
|
2922
|
+
checksType: transform(result.checksType),
|
|
2923
|
+
extendsType: transform(result.extendsType),
|
|
2924
|
+
trueType: transform(result.trueType),
|
|
2925
|
+
falseType: transform(result.falseType)
|
|
2926
|
+
}),
|
|
2927
|
+
JsdocTypeTypeParameter: (result, transform) => ({
|
|
2928
|
+
type: "JsdocTypeTypeParameter",
|
|
2929
|
+
name: transform(result.name),
|
|
2930
|
+
constraint: result.constraint !== void 0 ? transform(result.constraint) : void 0,
|
|
2931
|
+
defaultValue: result.defaultValue !== void 0 ? transform(result.defaultValue) : void 0
|
|
2932
|
+
}),
|
|
2933
|
+
JsdocTypeCallSignature: (result, transform) => ({
|
|
2934
|
+
type: "JsdocTypeCallSignature",
|
|
2935
|
+
parameters: result.parameters.map(transform),
|
|
2936
|
+
returnType: transform(result.returnType)
|
|
2937
|
+
}),
|
|
2938
|
+
JsdocTypeConstructorSignature: (result, transform) => ({
|
|
2939
|
+
type: "JsdocTypeConstructorSignature",
|
|
2940
|
+
parameters: result.parameters.map(transform),
|
|
2941
|
+
returnType: transform(result.returnType)
|
|
2942
|
+
}),
|
|
2943
|
+
JsdocTypeMethodSignature: (result, transform) => ({
|
|
2944
|
+
type: "JsdocTypeMethodSignature",
|
|
2945
|
+
name: result.name,
|
|
2946
|
+
parameters: result.parameters.map(transform),
|
|
2947
|
+
returnType: transform(result.returnType),
|
|
2948
|
+
meta: result.meta
|
|
2949
|
+
}),
|
|
2950
|
+
JsdocTypeIndexedAccessIndex: (result, transform) => ({
|
|
2951
|
+
type: "JsdocTypeIndexedAccessIndex",
|
|
2952
|
+
right: transform(result.right)
|
|
2953
|
+
}),
|
|
2954
|
+
JsdocTypeTemplateLiteral: (result, transform) => ({
|
|
2955
|
+
type: "JsdocTypeTemplateLiteral",
|
|
2956
|
+
literals: result.literals,
|
|
2957
|
+
interpolations: result.interpolations.map(transform)
|
|
2958
|
+
}),
|
|
2959
|
+
JsdocTypeComputedProperty: (result, transform) => {
|
|
2960
|
+
if (result.value.type.startsWith("JsdocType")) return {
|
|
2961
|
+
type: "JsdocTypeComputedProperty",
|
|
2962
|
+
value: transform(result.value)
|
|
2963
|
+
};
|
|
2964
|
+
else return {
|
|
2965
|
+
type: "JsdocTypeComputedProperty",
|
|
2966
|
+
value: structuredClone(result.value)
|
|
2967
|
+
};
|
|
2968
|
+
},
|
|
2969
|
+
JsdocTypeComputedMethod: (result, transform) => {
|
|
2970
|
+
if (result.value.type.startsWith("JsdocType")) return {
|
|
2971
|
+
type: "JsdocTypeComputedMethod",
|
|
2972
|
+
value: transform(result.value),
|
|
2973
|
+
optional: result.optional,
|
|
2974
|
+
parameters: result.parameters.map(transform),
|
|
2975
|
+
returnType: transform(result.returnType)
|
|
2976
|
+
};
|
|
2977
|
+
else return {
|
|
2978
|
+
type: "JsdocTypeComputedMethod",
|
|
2979
|
+
value: structuredClone(result.value),
|
|
2980
|
+
optional: result.optional,
|
|
2981
|
+
parameters: result.parameters.map(transform),
|
|
2982
|
+
returnType: transform(result.returnType)
|
|
2983
|
+
};
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
//#endregion
|
|
2988
|
+
//#region src/visitorKeys.ts
|
|
2989
|
+
const visitorKeys = {
|
|
2990
|
+
JsdocTypeAny: [],
|
|
2991
|
+
JsdocTypeBigInt: [],
|
|
2992
|
+
JsdocTypeFunction: [
|
|
2993
|
+
"typeParameters",
|
|
2994
|
+
"parameters",
|
|
2995
|
+
"returnType"
|
|
2996
|
+
],
|
|
2997
|
+
JsdocTypeGeneric: ["left", "elements"],
|
|
2998
|
+
JsdocTypeImport: ["element"],
|
|
2999
|
+
JsdocTypeIndexSignature: ["right"],
|
|
3000
|
+
JsdocTypeIntersection: ["elements"],
|
|
3001
|
+
JsdocTypeKeyof: ["element"],
|
|
3002
|
+
JsdocTypeKeyValue: ["right"],
|
|
3003
|
+
JsdocTypeMappedType: ["right"],
|
|
3004
|
+
JsdocTypeName: [],
|
|
3005
|
+
JsdocTypeInfer: ["element"],
|
|
3006
|
+
JsdocTypeUniqueSymbol: [],
|
|
3007
|
+
JsdocTypeNamePath: ["left", "right"],
|
|
3008
|
+
JsdocTypeNotNullable: ["element"],
|
|
3009
|
+
JsdocTypeNull: [],
|
|
3010
|
+
JsdocTypeNullable: ["element"],
|
|
3011
|
+
JsdocTypeNumber: [],
|
|
3012
|
+
JsdocTypeObject: ["elements"],
|
|
3013
|
+
JsdocTypeObjectField: ["key", "right"],
|
|
3014
|
+
JsdocTypeJsdocObjectField: ["left", "right"],
|
|
3015
|
+
JsdocTypeOptional: ["element"],
|
|
3016
|
+
JsdocTypeParenthesis: ["element"],
|
|
3017
|
+
JsdocTypeSpecialNamePath: [],
|
|
3018
|
+
JsdocTypeStringValue: [],
|
|
3019
|
+
JsdocTypeSymbol: ["element"],
|
|
3020
|
+
JsdocTypeTuple: ["elements"],
|
|
3021
|
+
JsdocTypeTypeof: ["element"],
|
|
3022
|
+
JsdocTypeUndefined: [],
|
|
3023
|
+
JsdocTypeUnion: ["elements"],
|
|
3024
|
+
JsdocTypeUnknown: [],
|
|
3025
|
+
JsdocTypeVariadic: ["element"],
|
|
3026
|
+
JsdocTypeProperty: [],
|
|
3027
|
+
JsdocTypePredicate: ["left", "right"],
|
|
3028
|
+
JsdocTypeAsserts: ["left", "right"],
|
|
3029
|
+
JsdocTypeReadonlyArray: ["element"],
|
|
3030
|
+
JsdocTypeAssertsPlain: ["element"],
|
|
3031
|
+
JsdocTypeConditional: [
|
|
3032
|
+
"checksType",
|
|
3033
|
+
"extendsType",
|
|
3034
|
+
"trueType",
|
|
3035
|
+
"falseType"
|
|
3036
|
+
],
|
|
3037
|
+
JsdocTypeTypeParameter: [
|
|
3038
|
+
"name",
|
|
3039
|
+
"constraint",
|
|
3040
|
+
"defaultValue"
|
|
3041
|
+
],
|
|
3042
|
+
JsdocTypeCallSignature: [
|
|
3043
|
+
"typeParameters",
|
|
3044
|
+
"parameters",
|
|
3045
|
+
"returnType"
|
|
3046
|
+
],
|
|
3047
|
+
JsdocTypeConstructorSignature: [
|
|
3048
|
+
"typeParameters",
|
|
3049
|
+
"parameters",
|
|
3050
|
+
"returnType"
|
|
3051
|
+
],
|
|
3052
|
+
JsdocTypeMethodSignature: [
|
|
3053
|
+
"typeParameters",
|
|
3054
|
+
"parameters",
|
|
3055
|
+
"returnType"
|
|
3056
|
+
],
|
|
3057
|
+
JsdocTypeIndexedAccessIndex: ["right"],
|
|
3058
|
+
JsdocTypeTemplateLiteral: ["interpolations"],
|
|
3059
|
+
JsdocTypeComputedProperty: ["value"],
|
|
3060
|
+
JsdocTypeComputedMethod: [
|
|
3061
|
+
"value",
|
|
3062
|
+
"typeParameters",
|
|
3063
|
+
"parameters",
|
|
3064
|
+
"returnType"
|
|
3065
|
+
]
|
|
3066
|
+
};
|
|
3067
|
+
//#endregion
|
|
3068
|
+
//#region src/traverse.ts
|
|
3069
|
+
function _traverse(node, parentNode, property, index, onEnter, onLeave) {
|
|
3070
|
+
onEnter?.(node, parentNode, property, index);
|
|
3071
|
+
const keysToVisit = visitorKeys[node.type];
|
|
3072
|
+
for (const key of keysToVisit) {
|
|
3073
|
+
const value = node[key];
|
|
3074
|
+
if (value !== void 0) {
|
|
3075
|
+
if (Array.isArray(value)) for (const [index, element] of value.entries()) _traverse(element, node, key, index, onEnter, onLeave);
|
|
3076
|
+
else if (value !== null && typeof value === "object" && "type" in value) _traverse(value, node, key, void 0, onEnter, onLeave);
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
onLeave?.(node, parentNode, property, index);
|
|
3080
|
+
}
|
|
3081
|
+
/**
|
|
3082
|
+
* A function to traverse an AST. It traverses it depth first.
|
|
3083
|
+
* @param node the node to start traversing at.
|
|
3084
|
+
* @param onEnter node visitor function that will be called on entering the node. This corresponds to preorder traversing.
|
|
3085
|
+
* @param onLeave node visitor function that will be called on leaving the node. This corresponds to postorder traversing.
|
|
3086
|
+
*/
|
|
3087
|
+
function traverse(node, onEnter, onLeave) {
|
|
3088
|
+
_traverse(node, void 0, void 0, void 0, onEnter, onLeave);
|
|
3089
|
+
}
|
|
3090
|
+
//#endregion
|
|
3091
|
+
export { catharsisTransform, identityTransformRules, jtpTransform, parse, parseName, parseNamePath, stringify, stringifyRules, transform, traverse, tryParse, visitorKeys };
|