@apify/docusaurus-plugin-typedoc-api 4.3.11 → 4.4.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.
@@ -1,14 +1,16 @@
1
+ /* eslint-disable max-classes-per-file */
1
2
  import { REPO_ROOT_PLACEHOLDER, TYPEDOC_KINDS } from './consts';
2
3
  import { InheritanceGraph } from './inheritance';
3
4
  import { PythonTypeResolver } from './type-parsing';
4
5
  import type {
5
6
  DocspecDocstring,
6
7
  DocspecObject,
8
+ OID,
7
9
  TypeDocDocstring,
8
10
  TypeDocObject,
9
11
  TypeDocType,
10
12
  } from './types';
11
- import { getGroupName, getOID, isHidden, isOverload, projectUsesDocsGroupDecorator, sortChildren } from './utils';
13
+ import { getGroupName, isHidden, isOverload, projectUsesDocsGroupDecorator, sortChildren } from './utils';
12
14
 
13
15
  interface TransformObjectOptions {
14
16
  /**
@@ -32,14 +34,58 @@ interface DocspecTransformerOptions {
32
34
  moduleShortcuts?: Record<string, string>;
33
35
  }
34
36
 
37
+ export class SymbolIdTracker {
38
+ symbolIdMap: Record<number, { qualifiedName: string; sourceFileName: string }> = {};
39
+
40
+ private idToReference: Record<number, TypeDocObject> = {};
41
+
42
+ private oidGenerator = this.generateOID();
43
+
44
+ /**
45
+ * Returns automatically incrementing OID. Every call to this function will return a new unique OID.
46
+ * @returns {number} The OID.
47
+ */
48
+ getNewId(): OID {
49
+ return this.oidGenerator.next().value as OID;
50
+ }
51
+
52
+ addNewReference(object: TypeDocObject) {
53
+ if( object.sources?.[0]?.fileName ) {
54
+ this.symbolIdMap[object.id] = {
55
+ qualifiedName: object.name,
56
+ sourceFileName: object.sources?.[0]?.fileName,
57
+ };
58
+ }
59
+
60
+ this.idToReference[object.id] = object;
61
+ }
62
+
63
+ getMethodSignatures() {
64
+ return Object.values(this.idToReference).filter(({ kindString }) => kindString === 'Call signature');
65
+ }
66
+
67
+ getIdByName(name: string) {
68
+ return Object.entries(this.symbolIdMap).find(([, { qualifiedName }]) => qualifiedName === name)?.[0];
69
+ }
70
+
71
+ getTypeDocById(id: number) {
72
+ return this.idToReference[id];
73
+ }
74
+
75
+ private *generateOID() {
76
+ let id = 1;
77
+ while (true) {
78
+ yield id++;
79
+ }
80
+ }
81
+ };
82
+
35
83
  export class DocspecTransformer {
36
84
  private pythonTypeResolver: PythonTypeResolver;
37
85
 
38
- private inheritanceGraph: InheritanceGraph = new InheritanceGraph();
86
+ private inheritanceGraph: InheritanceGraph;
39
87
 
40
- private symbolIdMap: Record<number, { qualifiedName: string; sourceFileName: string }> = {};
41
-
42
- private namesToIds: Record<string, number> = {};
88
+ private symbolIdTracker: SymbolIdTracker;
43
89
 
44
90
  private moduleShortcuts: Record<string, string>;
45
91
 
@@ -54,6 +100,8 @@ export class DocspecTransformer {
54
100
 
55
101
  constructor({ moduleShortcuts }: DocspecTransformerOptions) {
56
102
  this.pythonTypeResolver = new PythonTypeResolver();
103
+ this.symbolIdTracker = new SymbolIdTracker();
104
+ this.inheritanceGraph = new InheritanceGraph(this.symbolIdTracker);
57
105
  this.moduleShortcuts = moduleShortcuts ?? {};
58
106
  }
59
107
 
@@ -74,7 +122,7 @@ export class DocspecTransformer {
74
122
  line: 1,
75
123
  },
76
124
  ],
77
- symbolIdMap: this.symbolIdMap,
125
+ symbolIdMap: this.symbolIdTracker.symbolIdMap,
78
126
  };
79
127
 
80
128
  this.settings.useDocsGroup = projectUsesDocsGroupDecorator(
@@ -93,16 +141,9 @@ export class DocspecTransformer {
93
141
  this.inheritanceGraph.resolveInheritance();
94
142
  this.pythonTypeResolver.resolveTypes();
95
143
 
96
- this.namesToIds = Object.entries(this.symbolIdMap).reduce<Record<string, number>>(
97
- (acc, [id, { qualifiedName }]) => {
98
- acc[qualifiedName] = Number(id);
99
- return acc;
100
- },
101
- {},
102
- );
103
-
104
144
  this.fixRefs(typedocApiReference);
105
145
  sortChildren(typedocApiReference);
146
+ this.unpackKwargs();
106
147
 
107
148
  return typedocApiReference;
108
149
  }
@@ -119,6 +160,29 @@ export class DocspecTransformer {
119
160
  this.contextStack.push(context);
120
161
  }
121
162
 
163
+ private unpackKwargs() {
164
+ const signatures = this.symbolIdTracker.getMethodSignatures();
165
+
166
+ for (const sig of signatures) {
167
+ const unpackedParams: TypeDocObject[] = [];
168
+ for (const param of sig.parameters ?? []) {
169
+ if (param.type.type === 'literal' || param.type.type === 'reference' && param.type?.name !== 'Unpack') {
170
+ unpackedParams.push(param);
171
+ // eslint-disable-next-line no-continue
172
+ continue;
173
+ }
174
+
175
+ const typedDict = this.symbolIdTracker.getTypeDocById((param.type.typeArguments as { target: number }[])[0].target);
176
+
177
+ unpackedParams.push(
178
+ ...typedDict.children.map((x) => ({...x, flags: { 'keyword-only': true, optional: true } })),
179
+ )
180
+ }
181
+
182
+ sig.parameters = unpackedParams;
183
+ }
184
+ }
185
+
122
186
  /**
123
187
  * Recursively traverse the Typedoc structure and fix the references to the named entities.
124
188
  *
@@ -128,10 +192,8 @@ export class DocspecTransformer {
128
192
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
193
  private fixRefs(obj: Record<string, any>) {
130
194
  for (const key of Object.keys(obj)) {
131
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
132
- if (key === 'name' && obj?.type === 'reference' && this.namesToIds[obj?.name]) {
133
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
134
- obj.target = this.namesToIds[obj?.name];
195
+ if (key === 'name' && obj?.type === 'reference' && this.symbolIdTracker.getIdByName(obj?.name as string ?? '')) {
196
+ obj.target = this.symbolIdTracker.getIdByName(obj?.name as string ?? '');
135
197
  }
136
198
  if (typeof obj[key] === 'object' && obj[key] !== null) {
137
199
  this.fixRefs(obj[key] as TypeDocObject);
@@ -140,7 +202,7 @@ export class DocspecTransformer {
140
202
  }
141
203
 
142
204
  private makeMethodSignature(docspecObject: DocspecObject, docstring: TypeDocDocstring): TypeDocObject['signatures'][number] {
143
- return {
205
+ const methodTypeDoc: TypeDocObject = {
144
206
  comment: docstring.text
145
207
  ? {
146
208
  blockTags: docstring?.returns
@@ -155,37 +217,47 @@ export class DocspecTransformer {
155
217
  }
156
218
  : undefined,
157
219
  flags: {},
158
- id: getOID(),
220
+ id: this.symbolIdTracker.getNewId(),
159
221
  kind: 4096,
160
222
  kindString: 'Call signature',
161
223
  modifiers: docspecObject.modifiers ?? [],
162
224
  name: docspecObject.name,
163
225
  parameters: docspecObject.args
164
226
  ?.filter((arg) => arg.name !== 'self' && arg.name !== 'cls')
165
- .map((arg) => ({
166
- comment: docstring.args?.[arg.name]
167
- ? {
168
- summary: [
169
- {
170
- kind: 'text',
171
- text: docstring.args[arg.name],
172
- },
173
- ],
174
- }
175
- : undefined,
176
- defaultValue: arg.default_value as string,
177
- flags: {
178
- isOptional: arg.datatype?.includes('Optional') || arg.default_value !== undefined,
179
- 'keyword-only': arg.type === 'KEYWORD_ONLY',
180
- },
181
- id: getOID(),
182
- kind: 32_768,
183
- kindString: 'Parameter',
184
- name: arg.name,
185
- type: this.pythonTypeResolver.registerType(arg.datatype),
186
- })),
227
+ .map((arg) => {
228
+ const paramTypeDoc: TypeDocObject = {
229
+ comment: docstring.args?.[arg.name]
230
+ ? {
231
+ summary: [
232
+ {
233
+ kind: 'text',
234
+ text: docstring.args[arg.name],
235
+ },
236
+ ],
237
+ }
238
+ : undefined,
239
+ defaultValue: arg.default_value as string,
240
+ flags: {
241
+ isOptional: arg.datatype?.includes('Optional') || arg.default_value !== undefined,
242
+ 'keyword-only': arg.type === 'KEYWORD_ONLY',
243
+ },
244
+ id: this.symbolIdTracker.getNewId(),
245
+ kind: 32_768,
246
+ kindString: 'Parameter',
247
+ name: arg.name,
248
+ type: this.pythonTypeResolver.registerType(arg.datatype),
249
+ }
250
+
251
+ this.symbolIdTracker.addNewReference(paramTypeDoc);
252
+
253
+ return paramTypeDoc;
254
+ }),
187
255
  type: this.pythonTypeResolver.registerType(docspecObject.return_type),
188
256
  };
257
+
258
+ this.symbolIdTracker.addNewReference(methodTypeDoc);
259
+
260
+ return methodTypeDoc;
189
261
  }
190
262
 
191
263
  /**
@@ -200,6 +272,10 @@ export class DocspecTransformer {
200
272
  parentTypeDoc,
201
273
  moduleName,
202
274
  }: TransformObjectOptions) {
275
+ if (currentDocspecNode?.datatype === 'TypeAlias') {
276
+ this.pythonTypeResolver.registerType(currentDocspecNode.value as string, currentDocspecNode.name);
277
+ }
278
+
203
279
  if (isHidden(currentDocspecNode)) {
204
280
  for (const docspecMember of currentDocspecNode.members ?? []) {
205
281
  this.walkAndTransform({
@@ -225,12 +301,7 @@ export class DocspecTransformer {
225
301
  return;
226
302
  }
227
303
 
228
- const currentId = getOID();
229
-
230
- this.symbolIdMap[currentId] = {
231
- qualifiedName: currentDocspecNode.name,
232
- sourceFileName: filePathInRepo,
233
- };
304
+ const currentId = this.symbolIdTracker.getNewId();
234
305
 
235
306
  // Get the module name of the member, and check if it has a shortcut (reexport from an ancestor module)
236
307
  const fullName = `${moduleName}.${currentDocspecNode.name}`;
@@ -273,6 +344,8 @@ export class DocspecTransformer {
273
344
  type: typedocType,
274
345
  };
275
346
 
347
+ this.symbolIdTracker.addNewReference(currentTypedocNode);
348
+
276
349
  if (currentTypedocNode.kindString === 'Method') {
277
350
  currentTypedocNode.signatures = [
278
351
  this.makeMethodSignature(currentDocspecNode, currentDocspecNode.parsedDocstring),
@@ -17,17 +17,23 @@ const PYTHON_SCRIPT_FILEPATH = path.join(__dirname, '..', '..', '..', '..', 'pyt
17
17
  export class PythonTypeResolver {
18
18
  private typedocTypes: TypeDocType[] = [];
19
19
 
20
+ private aliases: Record<string, string> = {};
21
+
20
22
  /**
21
23
  * Register a new Python type to be resolved.
22
24
  *
23
25
  * Given a string representation of the type, returns a Typedoc type object.
24
26
  */
25
- registerType(docspecType?: DocspecType): TypeDocType {
27
+ registerType(docspecType?: DocspecType, aliasName?: string): TypeDocType {
26
28
  const newType: TypeDocType = {
27
29
  name: docspecType?.replaceAll(/#.*/g, '').replaceAll('\n', '').trim() ?? 'Undefined',
28
30
  type: 'reference',
29
31
  };
30
32
 
33
+ if (aliasName) {
34
+ this.aliases[aliasName] = newType.name;
35
+ }
36
+
31
37
  this.typedocTypes.push(newType);
32
38
  return newType;
33
39
  }
@@ -66,7 +72,9 @@ export class PythonTypeResolver {
66
72
 
67
73
  for (const originalType of this.typedocTypes) {
68
74
  if (originalType.type === 'reference') {
69
- const parsedType = parsedTypes[originalType.name];
75
+ // The verbatim name of the type will always be in `parsedTypes`.
76
+ // If it references a `TypeAlias`, it should have a priority.
77
+ const parsedType = parsedTypes[this.aliases[originalType.name]] ?? parsedTypes[originalType.name];
70
78
 
71
79
  if (parsedType) {
72
80
  for (const key of Object.keys(parsedType)) {
@@ -1,23 +1,6 @@
1
1
  /* eslint-disable sort-keys */
2
2
  import { GROUP_ORDER, TYPEDOC_KINDS } from './consts';
3
- import type { DocspecObject, OID, TypeDocObject } from './types';
4
-
5
- function* generateOID() {
6
- let id = 1;
7
- while (true) {
8
- yield id++;
9
- }
10
- }
11
-
12
- const oidGenerator = generateOID();
13
-
14
- /**
15
- * Returns automatically incrementing OID. Every call to this function will return a new unique OID.
16
- * @returns {number} The OID.
17
- */
18
- export function getOID(): OID {
19
- return oidGenerator.next().value as OID;
20
- }
3
+ import type { DocspecObject, TypeDocObject } from './types';
21
4
 
22
5
  /**
23
6
  * Given a TypeDoc object, returns the name of the group this object belongs to.