@satisfactory-dev/docdown 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/entry.js ADDED
@@ -0,0 +1,676 @@
1
+ /*!
2
+ * docdown
3
+ * Copyright 2011-2016 John-David Dalton
4
+ * Copyright 2026 SignpostMarv
5
+ * Available under MIT license
6
+ */
7
+
8
+ import {
9
+ parse,
10
+ } from 'jsdoc-type-pratt-parser';
11
+
12
+ import Alias from './alias.js';
13
+ import util from './util.js';
14
+
15
+ /*----------------------------------------------------------------------------*/
16
+
17
+ /**
18
+ * Gets the param type of `tag`.
19
+ *
20
+ * @private
21
+ * @param {Object} tag The param tag to inspect.
22
+ * @returns {string} Returns the param type.
23
+ */
24
+ function getParamType(tag) {
25
+ var result = '';
26
+
27
+ if (tag.type == 'JsdocTypeParenthesis') {
28
+ return getParamType(tag.element);
29
+ } else if (
30
+ tag.type == 'JsdocTypeGeneric' &&
31
+ tag.left.type == 'JsdocTypeName'&&
32
+ tag.left.value == 'Array'
33
+ ) {
34
+ return `${getParamType(tag.elements[0])}[]`;
35
+ } else if (tag.type === 'JsdocTypeUndefined') {
36
+ return 'undefined';
37
+ } else if (tag.type == 'JsdocTypeNumber') {
38
+ return tag.value.toString();
39
+ } else if (tag.type === 'JsdocTypeStringValue') {
40
+ return `'${tag.value}'`;
41
+ } else if (tag.type === 'JsdocTypeTuple') {
42
+ return `[${tag.elements.map(getParamType).join(', ')}]`;
43
+ }
44
+
45
+ let type;
46
+
47
+ try {
48
+ type = parse(tag.type.startsWith('JsdocType') ? tag.value : tag.type, 'jsdoc');
49
+ } catch (err) {
50
+ type = parse(tag.type, 'typescript');
51
+ }
52
+
53
+ switch (type.type) {
54
+ case 'JsdocTypeAny':
55
+ result = '*';
56
+ break;
57
+
58
+ case 'JsdocTypeName':
59
+ result = tag.type === type.type ? tag.value : type.value;
60
+ break;
61
+
62
+ case 'JsdocTypeVariadic':
63
+ result = `...${getParamType(type.element)}`;
64
+ break;
65
+
66
+ case 'JsdocTypeUnion':
67
+ result = type.elements
68
+ .map(getParamType)
69
+ .sort(util.compareNatural)
70
+ .join('|')
71
+ ;
72
+ break;
73
+ }
74
+
75
+ return type.type == 'JsdocTypeUnion'
76
+ ? ('(' + result + ')')
77
+ : result;
78
+ }
79
+
80
+ /**
81
+ * Gets an `entry` tag by `tagName`.
82
+ *
83
+ * @private
84
+ * @param {Entry} entry The entry to inspect.
85
+ * @param {string} tagName The name of the tag.
86
+ * @returns {null|Tag} Returns the tag.
87
+ */
88
+ function getTag(entry, tagName) {
89
+ return entry.parsed.tags.find(({tag}) => tag == tagName) || null;
90
+ }
91
+
92
+ /**
93
+ * Gets an `entry` tag value by `tagName`.
94
+ *
95
+ * @private
96
+ * @param {Entry} entry The entry to inspect.
97
+ * @param {string} tagName The name of the tag.
98
+ * @returns {string} Returns the tag value.
99
+ */
100
+ function getValue(entry, tagName) {
101
+ var parsed = entry.parsed,
102
+ /** @type {string|undefined} */
103
+ result = parsed.description,
104
+ tag = getTag(entry, tagName);
105
+
106
+ if (tagName == 'alias') {
107
+ result = tag ? tag.name : '';
108
+ }
109
+ else if (tagName == 'type') {
110
+ result = tag ? (tag.name || tag.type) : '';
111
+ }
112
+ else if (tagName != 'description') {
113
+ result = tag ? (tag.name || tag.description) : '';
114
+ }
115
+ return tagName == 'example'
116
+ ? `${result || ''}`
117
+ : util.format(result);
118
+ }
119
+
120
+ /**
121
+ * Checks if `entry` has a tag of `tagName`.
122
+ *
123
+ * @private
124
+ * @param {Entry} entry The entry to inspect.
125
+ * @param {string} tagName The name of the tag.
126
+ * @returns {boolean} Returns `true` if the tag is found, else `false`.
127
+ */
128
+ function hasTag(entry, tagName) {
129
+ return getTag(entry, tagName) !== null;
130
+ }
131
+
132
+ /**
133
+ * Converts CR+LF line endings to LF.
134
+ *
135
+ * @private
136
+ * @param {string} string The string to convert.
137
+ * @returns {string} Returns the converted string.
138
+ */
139
+ function normalizeEOL(string) {
140
+ return string.replace(/\r\n/g, '\n');
141
+ }
142
+
143
+ /*----------------------------------------------------------------------------*/
144
+
145
+ class Entry {
146
+ /**
147
+ * @readonly
148
+ * @type {string}
149
+ */
150
+ entry;
151
+
152
+ /**
153
+ * @readonly
154
+ * @type {string}
155
+ */
156
+ lang;
157
+
158
+ /**
159
+ * @readonly
160
+ * @type {JS}
161
+ */
162
+ parsed;
163
+
164
+ /**
165
+ * The Entry constructor.
166
+ *
167
+ * @param {string} entry The documentation entry to analyse.
168
+ * @param {string} source The source code.
169
+ * @param {string} [lang='js'] The language highlighter used for code examples.
170
+ */
171
+ constructor(entry, source, lang) {
172
+ entry = normalizeEOL(entry);
173
+
174
+ this.entry = entry;
175
+ this.lang = lang == null ? 'js' : lang;
176
+ this.parsed = util.parse(entry.replace(/(?:(?:\*)\/\s*.+)$/, '*/'));
177
+ this.source = normalizeEOL(source);
178
+ }
179
+
180
+ /**
181
+ * Extracts the documentation entries from source code.
182
+ *
183
+ * @param {string} source The source code.
184
+ * @returns {string[]} Returns the array of entries.
185
+ *
186
+ * @todo fix this so it does AST traversl to handle exports properly
187
+ * @see satisfactory-dev/docdown#1
188
+ */
189
+ static getEntries(source) {
190
+ return `${source}`.match(/\/\*\*(?![-!])[\s\S]*?\*\/\s*.+/g) || [];
191
+ }
192
+
193
+ /** @type {Array|undefined} */
194
+ _aliases;
195
+
196
+ /**
197
+ * Extracts the entry's `alias` objects.
198
+ *
199
+ * @param {number} index The index of the array value to return.
200
+ * @returns {Array|string} Returns the entry's `alias` objects.
201
+ */
202
+ getAliases(index) {
203
+ if (this._aliases === undefined) {
204
+ var owner = this;
205
+ this._aliases = getValue(this, 'alias')
206
+ .split(/,\s*/)
207
+ .filter((maybe) => !!maybe)
208
+ .sort(util.compareNatural)
209
+ .map(function(value) { return new Alias(value, owner); })
210
+ ;
211
+ }
212
+ var result = this._aliases;
213
+ return index === undefined ? result : result[index];
214
+ }
215
+
216
+ /**
217
+ * @type {string|undefined}
218
+ */
219
+ #getCall = undefined;
220
+
221
+ /**
222
+ * Extracts the function call from the entry.
223
+ *
224
+ * @returns {string} Returns the function call.
225
+ */
226
+ getCall() {
227
+ if (this.#getCall == undefined) {
228
+ var result = util.regexExecIndex(/\*\/\s*(?:function\s+)?([^\s(]+)\s*\(/, this.entry).trim();
229
+ if (!result) {
230
+ result = util.regexExecIndex(/\*\/\s*(.*?)[:=,]/, this.entry).trim();
231
+ result = /['"]$/.test(result)
232
+ ? result.replace(/^['"]*([^'"].*[^'"])['"]*$/, '$1')
233
+ : result.split('.').pop().split(/^(?:const|let|var) /).pop();
234
+ }
235
+ var name = getValue(this, 'name') || result;
236
+ if (!this.isFunction()) {
237
+ return this.#getCall = name;
238
+ }
239
+ var params = this.getParams();
240
+ result = [result];
241
+
242
+ // Compile the function call syntax.
243
+ params.forEach(function(param) {
244
+ var paramValue = param[1],
245
+ parentParam = util.regexExecIndex(/\w+(?=\.[\w.]+)/, paramValue, 0);
246
+
247
+ var parentIndex = params.findIndex(function(param) {
248
+ return param[1].replace(/^[[\]]*([^[\]].*[^[\]])[[\]]*$/, '$1').split(/\s*=/)[0] == parentParam;
249
+ });
250
+
251
+ // Skip params that are properties of other params (e.g. `options.leading`).
252
+ if (((params[parentIndex] || [])[0] || undefined) != 'Object') {
253
+ result.push(paramValue);
254
+ }
255
+ });
256
+
257
+ // Format the function call.
258
+ this.#getCall = name + '(' + result.slice(1).join(', ') + ')';
259
+ }
260
+
261
+ return this.#getCall;
262
+ }
263
+
264
+ /** @type {string|undefined} */
265
+ #getCategory;
266
+
267
+ /**
268
+ * Extracts the entry's `category` data.
269
+ *
270
+ * @returns {string} Returns the entry's `category` data.
271
+ */
272
+ getCategory() {
273
+ if (this.#getCategory == undefined) {
274
+ var result = getValue(this, 'category');
275
+ this.#getCategory = result || (this.getType() == 'Function' ? 'Methods' : 'Properties');
276
+ }
277
+
278
+ return this.#getCategory;
279
+ }
280
+
281
+ /** @type {string|undefined} */
282
+ #getDesc;
283
+
284
+ /**
285
+ * Extracts the entry's description.
286
+ *
287
+ * @returns {string} Returns the entry's description.
288
+ */
289
+ getDesc() {
290
+ if (this.#getDesc == undefined) {
291
+ var type = this.getType(),
292
+ result = getValue(this, 'description');
293
+
294
+ this.#getDesc = (!result || type == 'Function' || type == 'unknown')
295
+ ? result
296
+ : ('(' + util.deparenthesize(type.replace(/\|/g, ', ')) + '): ' + result);
297
+ }
298
+
299
+ return this.#getDesc;
300
+ }
301
+
302
+ /** @type {string|undefined} */
303
+ #getExample;
304
+
305
+ /**
306
+ * Extracts the entry's `example` data.
307
+ *
308
+ * @returns {string} Returns the entry's `example` data.
309
+ */
310
+ getExample() {
311
+ if (this.#getExample == undefined) {
312
+ var result = getValue(this, 'example');
313
+ this.#getExample = result ? ('```' + this.lang + '\n' + result.trim() + '\n```') : '';
314
+ }
315
+
316
+ return this.#getExample;
317
+ }
318
+
319
+ /** @type {string|undefined} */
320
+ #getHash;
321
+
322
+ /**
323
+ * Extracts the entry's hash value for permalinking.
324
+ *
325
+ * @param {string} [style] The hash style.
326
+ * @returns {string} Returns the entry's hash value (without a hash itself).
327
+ */
328
+ getHash(style) {
329
+ if (this.#getHash == undefined) {
330
+ var result = `${this.getMembers(0) || ''}`;
331
+ if (style == 'github') {
332
+ if (result) {
333
+ result += this.isPlugin() ? 'prototype' : '';
334
+ }
335
+ result += this.getCall();
336
+
337
+ return this.#getHash = result
338
+ .replace(/[\\.=|'"(){}\[\]\t ]/g, '')
339
+ .replace(/[#,]+/g, '-')
340
+ .toLowerCase();
341
+ }
342
+ if (result) {
343
+ result += '-' + (this.isPlugin() ? 'prototype-' : '');
344
+ }
345
+ result += this.isAlias() ? this.getOwner().getName() : this.getName();
346
+
347
+ return this.#getHash = result
348
+ .replace(/\./g, '-')
349
+ .replace(/^_-/, '');
350
+ }
351
+
352
+ return this.#getHash;
353
+ }
354
+
355
+ /** @type {number|undefined} */
356
+ #getLineNumber;
357
+
358
+ /**
359
+ * Resolves the entry's line number.
360
+ *
361
+ * @returns {number} Returns the entry's line number.
362
+ */
363
+ getLineNumber() {
364
+ if (this.#getLineNumber == undefined) {
365
+ var lines = this.source
366
+ .slice(0, this.source.indexOf(this.entry) + this.entry.length)
367
+ .match(/\n/g)
368
+ .slice(1);
369
+
370
+ // Offset by 2 because the first line number is before a line break and the
371
+ // last line doesn't include a line break.
372
+ this.#getLineNumber = lines.length + 2;
373
+ }
374
+
375
+ return this.#getLineNumber;
376
+ }
377
+
378
+ /** @type {Array|undefined} */
379
+ _members;
380
+
381
+ /**
382
+ * Extracts the entry's `member` data.
383
+ *
384
+ * @param {number} [index] The index of the array value to return.
385
+ * @returns {Array|string|undefined} Returns the entry's `member` data.
386
+ */
387
+ getMembers(index) {
388
+ if (this._members === undefined) {
389
+ this._members = (getValue(this, 'member') || getValue(this, 'memberOf'))
390
+ .split(/,\s*/)
391
+ .filter((maybe) => !!maybe)
392
+ .sort(util.compareNatural)
393
+ ;
394
+ }
395
+ var result = this._members;
396
+ return index === undefined ? result : result[index];
397
+ }
398
+
399
+ /** @type {string|undefined} */
400
+ #getName;
401
+
402
+ /**
403
+ * Extracts the entry's `name` data.
404
+ *
405
+ * @returns {string} Returns the entry's `name` data.
406
+ */
407
+ getName() {
408
+ if (this.#getName == undefined) {
409
+ this.#getName = hasTag(this, 'name')
410
+ ? getValue(this, 'name')
411
+ : `${this.getCall().split('(')[0]}`;
412
+ }
413
+
414
+ return this.#getName;
415
+ }
416
+
417
+ /** @type {Array|undefined} */
418
+ _params;
419
+
420
+ /**
421
+ * Extracts the entry's `param` data.
422
+ *
423
+ * @param {number} [index] The index of the array value to return.
424
+ * @returns {Array} Returns the entry's `param` data.
425
+ */
426
+ getParams(index) {
427
+ if (this._params === undefined) {
428
+ this._params = this.parsed.tags
429
+ .filter(({tag, name}) => tag == 'param' && name)
430
+ .map(function(tag) {
431
+ var defaultValue = tag['default'],
432
+ desc = util.format(tag.description),
433
+ name = `${tag.name}`,
434
+ type = getParamType(tag);
435
+
436
+ if (defaultValue != null) {
437
+ name += '=' + defaultValue;
438
+ }
439
+ if (tag.optional) {
440
+ name = '[' + name + ']';
441
+ }
442
+ return [type, name, desc];
443
+ });
444
+ }
445
+
446
+ var result = this._params;
447
+
448
+ return index === undefined ? result : result[index];
449
+ }
450
+
451
+ /** @type {Array|undefined} */
452
+ #getRelated;
453
+
454
+ /**
455
+ * Extracts the entry's `see` data.
456
+ *
457
+ * @returns {array} Returns the entry's `see` data as links.
458
+ */
459
+ getRelated() {
460
+ if (this.#getRelated == undefined) {
461
+ var relatedValues = getValue(this, 'see');
462
+ if (relatedValues && relatedValues.trim().length > 0) {
463
+ var relatedItems = relatedValues.split(',').map((relatedItem) => relatedItem.trim());
464
+ this.#getRelated = relatedItems.map((relatedItem) => '[' + relatedItem + '](#' + relatedItem + ')');
465
+ } else {
466
+ this.#getRelated = [];
467
+ }
468
+ }
469
+
470
+ return this.#getRelated;
471
+ }
472
+
473
+ /** @type {Array|undefined} */
474
+ #getReturns;
475
+
476
+ /**
477
+ * Extracts the entry's `returns` data.
478
+ *
479
+ * @returns {array} Returns the entry's `returns` data.
480
+ */
481
+ getReturns() {
482
+ if (this.#getReturns == undefined) {
483
+ var tag = getTag(this, 'returns');
484
+ var unionReturn = (tag && 'type' in tag) ? getParamType(tag) : undefined;
485
+ var
486
+ desc = `${(tag ? tag.description : undefined) || ''}`,
487
+ type = `${((tag && tag.type) ? tag.type.name : undefined) || ''}` || unionReturn || '*';
488
+
489
+ this.#getReturns = tag ? [type, desc] : [];
490
+ }
491
+
492
+ return this.#getReturns;
493
+ }
494
+
495
+ /** @type {string|undefined} */
496
+ #getSince;
497
+
498
+ /**
499
+ * Extracts the entry's `since` data.
500
+ *
501
+ * @returns {string} Returns the entry's `since` data.
502
+ */
503
+ getSince() {
504
+ if (this.#getSince == undefined) {
505
+ this.#getSince = getValue(this, 'since');
506
+ }
507
+
508
+ return this.#getSince;
509
+ }
510
+
511
+ /** @type {string|undefined} */
512
+ #getType;
513
+
514
+ /**
515
+ * Extracts the entry's `type` data.
516
+ *
517
+ * @returns {string} Returns the entry's `type` data.
518
+ */
519
+ getType() {
520
+ if (this.#getType == undefined) {
521
+ var result = getValue(this, 'type');
522
+ if (!result) {
523
+ return this.#getType = this.isFunction() ? 'Function' : 'unknown';
524
+ }
525
+
526
+ this.#getType = /^(?:array|function|object|regexp)$/.test(result)
527
+ ? `${result[0].toUpperCase()}${result.substring(1)}`
528
+ : result;
529
+ }
530
+
531
+ return this.#getType;
532
+ }
533
+
534
+ /**
535
+ * Checks if the entry is an alias.
536
+ *
537
+ * @type {Function}
538
+ * @returns {boolean} Returns `false`.
539
+ */
540
+ isAlias() {
541
+ return false;
542
+ }
543
+
544
+ /** @type {boolean|undefined} */
545
+ #isCtor;
546
+
547
+ /**
548
+ * Checks if the entry is a constructor.
549
+ *
550
+ * @returns {boolean} Returns `true` if a constructor, else `false`.
551
+ */
552
+ isCtor() {
553
+ if (this.#isCtor == undefined) {
554
+ this.#isCtor = hasTag(this, 'constructor');
555
+
556
+ }
557
+
558
+ return this.#isCtor;
559
+ }
560
+
561
+ /** @type {boolean|undefined} */
562
+ #isFunction;
563
+
564
+ /**
565
+ * Checks if the entry is a function reference.
566
+ *
567
+ * @returns {boolean} Returns `true` if the entry is a function reference, else `false`.
568
+ */
569
+ isFunction() {
570
+ if (this.#isFunction == undefined) {
571
+ this.#isFunction = !!(
572
+ this.isCtor() ||
573
+ this.getParams().length ||
574
+ this.getReturns().length ||
575
+ hasTag(this, 'function') ||
576
+ /\*\/\s*(?:function\s+)?[^\s(]+\s*\(/.test(this.entry)
577
+ );
578
+ }
579
+
580
+ return this.#isFunction;
581
+ }
582
+
583
+ /** @type {boolean|undefined} */
584
+ #isLicense;
585
+
586
+ /**
587
+ * Checks if the entry is a license.
588
+ *
589
+ * @returns {boolean} Returns `true` if a license, else `false`.
590
+ */
591
+ isLicense() {
592
+ if (this.#isLicense == undefined) {
593
+ this.#isLicense = hasTag(this, 'license');
594
+ }
595
+
596
+ return this.#isLicense;
597
+ }
598
+
599
+ /** @type {boolean|undefined} */
600
+ #isPlugin;
601
+
602
+ /**
603
+ * Checks if the entry *is* assigned to a prototype.
604
+ *
605
+ * @returns {boolean} Returns `true` if assigned to a prototype, else `false`.
606
+ */
607
+ isPlugin() {
608
+ if (this.#isPlugin == undefined) {
609
+ this.#isPlugin = (
610
+ !this.isCtor() &&
611
+ !this.isPrivate() &&
612
+ !this.isStatic()
613
+ );
614
+ }
615
+
616
+ return this.#isPlugin;
617
+ }
618
+
619
+ /** @type {boolean|undefined} */
620
+ #isPrivate;
621
+
622
+ /**
623
+ * Checks if the entry is private.
624
+ *
625
+ * @returns {boolean} Returns `true` if private, else `false`.
626
+ */
627
+ isPrivate() {
628
+ if (this.#isPrivate == undefined) {
629
+ this.#isPrivate = (
630
+ this.isLicense() ||
631
+ hasTag(this, 'private') ||
632
+ this.parsed.tags.length < 1
633
+ );
634
+ }
635
+
636
+ return this.#isPrivate;
637
+ }
638
+
639
+ /** @type {boolean|undefined} */
640
+ #isStatic;
641
+
642
+ /**
643
+ * Checks if the entry is *not* assigned to a prototype.
644
+ *
645
+ * @returns {boolean} Returns `true` if not assigned to a prototype, else `false`.
646
+ */
647
+ isStatic() {
648
+ if (this.#isStatic == undefined) {
649
+ var isPublic = !this.isPrivate(),
650
+ result = isPublic && hasTag(this, 'static');
651
+
652
+ // Get the result in cases where it isn't explicitly stated.
653
+ if (isPublic && !result) {
654
+ const parts = `${this.getMembers(0) || ''}`.split(/[#.]/);
655
+ var parent = parts[parts.length - 1];
656
+ if (!parent) {
657
+ return this.#isStatic = true;
658
+ }
659
+ var source = this.source;
660
+ Entry.getEntries(source).forEach(function(entry) {
661
+ entry = new Entry(entry, source);
662
+ if (entry.getName() == parent) {
663
+ result = !entry.isCtor();
664
+ return false;
665
+ }
666
+ });
667
+ }
668
+
669
+ return this.#isStatic = result;
670
+ }
671
+
672
+ return this.#isStatic;
673
+ }
674
+ }
675
+
676
+ export default Entry;