-
Notifications
You must be signed in to change notification settings - Fork 0
/
typedoc-plugin-property.mjs
133 lines (111 loc) · 5.06 KB
/
typedoc-plugin-property.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { readFileSync } from 'fs';
import { resolve } from 'path';
// eslint-disable-next-line import/no-unresolved
import { ArrayType, Converter, DeclarationReflection, IntrinsicType, ReflectionFlag, ReflectionKind, ReferenceType, UnionType } from 'typedoc';
/**
* Extract property types from JSDoc in a .js file.
*
* @param {string} filePath - The path to the .js file.
* @returns {Map<string, string>} A map of property names to types.
*/
function getProperties(filePath) {
const data = readFileSync(resolve(process.cwd(), filePath), 'utf-8');
const docBlocks = data.match(/\/\*\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g);
const properties = new Map();
if (docBlocks) {
docBlocks.forEach((block) => {
const propertyLines = block.match(/@property\s*\{[^}]+\}\s*[^*]*/g);
if (propertyLines) {
propertyLines.forEach((line) => {
const match = line.match(/@property\s*\{([^}]+)\}\s*(\w+)/);
if (match) {
let type = match[1].trim();
const name = match[2].trim();
// Simplify complex import types.
type = type.replace(/import\(['"]([^'"]+)['"]\)\.(\w+)/g, (_, p1, p2) => p2);
properties.set(name, type);
}
});
}
});
}
return properties;
}
/**
* This Typedoc plugin adds missing PlayCanvas API symbols to the Typedoc reflection graph. The
* symbols are missing because they are generated by `Object.defineProperty` in the PlayCanvas
* sourcebase. The TypeScript compiler is unable to detect them, either in the code or in the
* JSDoc comments (specified via \@property tags).
*
* @param {import('typedoc').Application} app - The Typedoc application.
*/
function load(app) {
const classes = new Map([
['StandardMaterial', './submodules/engine/src/scene/materials/standard-material.js']
]);
app.converter.on(Converter.EVENT_RESOLVE_BEGIN, (/** @type {import('typedoc').Context} */ context) => {
const getReference = (type) => {
const reflection = context.project.children[0].children.find(child => child.name === type && child.kind === ReflectionKind.Class);
if (!reflection) {
console.error(`Unable to find class ${type}`);
}
return reflection;
};
classes.forEach((filePath, className) => {
const reflection = getReference(className);
/**
* Returns the reference type matching the specified class name.
*
* @param {string} type - The class name.
* @returns {ReferenceType} The reference type.
*/
const getReferenceType = (type) => {
const reference = getReference(type);
return reference ? new ReferenceType(type, reference, context.project) : undefined;
};
/**
* Returns the Typedoc type matching the specified JSDoc type. This can include a union type (|).
*
* @param {string} type - The JSDoc type string.
* @returns {import('typedoc').Type} The Typedoc type.
*/
const getType = (type) => {
if (type.includes('|')) {
const types = type.split('|');
return new UnionType(types.map(type => getType(type)));
}
switch (type) {
case 'null':
return new IntrinsicType('null');
case 'boolean':
return new IntrinsicType('boolean');
case 'number':
return new IntrinsicType('number');
case 'number[]':
return new ArrayType(new IntrinsicType('number'));
case 'string':
return new IntrinsicType('string');
default:
return getReferenceType(type);
}
};
const properties = getProperties(filePath);
// Get just the @property definitions from the class' JSDoc block
const blockTags = reflection.comment.blockTags.filter(blockTag => blockTag.tag === '@property');
// Convert all @property tags on StandardMaterial to actual child properties of StandardMaterial
for (const blockTag of blockTags) {
const newProperty = new DeclarationReflection(blockTag.name, ReflectionKind.Property, reflection);
const type = properties.get(blockTag.name);
newProperty.type = getType(type);
// Mark the new property as public
newProperty.setFlag(ReflectionFlag.Public, true);
// Add the new property to the class
if (!reflection.children) {
reflection.children = [];
}
reflection.addChild(newProperty);
}
});
});
}
export { load };