@apify/docusaurus-plugin-typedoc-api 4.3.12 → 4.4.1

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();
39
-
40
- private symbolIdMap: Record<number, { qualifiedName: string; sourceFileName: string }> = {};
86
+ private inheritanceGraph: InheritanceGraph;
41
87
 
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,36 @@ 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 !== '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(
176
+ (
177
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
178
+ param.type.typeArguments[0]?.target
179
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument
180
+ ?? this.symbolIdTracker.getIdByName(param.type.typeArguments[0]?.name)
181
+ ) as number
182
+ );
183
+
184
+ unpackedParams.push(
185
+ ...(typedDict?.children.map((x) => ({...x, flags: { 'keyword-only': true, optional: true } })) ?? [])
186
+ )
187
+ }
188
+
189
+ sig.parameters = unpackedParams;
190
+ }
191
+ }
192
+
122
193
  /**
123
194
  * Recursively traverse the Typedoc structure and fix the references to the named entities.
124
195
  *
@@ -128,10 +199,8 @@ export class DocspecTransformer {
128
199
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
200
  private fixRefs(obj: Record<string, any>) {
130
201
  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];
202
+ if (key === 'name' && obj?.type === 'reference' && this.symbolIdTracker.getIdByName(obj?.name as string ?? '')) {
203
+ obj.target = this.symbolIdTracker.getIdByName(obj?.name as string ?? '');
135
204
  }
136
205
  if (typeof obj[key] === 'object' && obj[key] !== null) {
137
206
  this.fixRefs(obj[key] as TypeDocObject);
@@ -140,7 +209,7 @@ export class DocspecTransformer {
140
209
  }
141
210
 
142
211
  private makeMethodSignature(docspecObject: DocspecObject, docstring: TypeDocDocstring): TypeDocObject['signatures'][number] {
143
- return {
212
+ const methodTypeDoc: TypeDocObject = {
144
213
  comment: docstring.text
145
214
  ? {
146
215
  blockTags: docstring?.returns
@@ -155,37 +224,47 @@ export class DocspecTransformer {
155
224
  }
156
225
  : undefined,
157
226
  flags: {},
158
- id: getOID(),
227
+ id: this.symbolIdTracker.getNewId(),
159
228
  kind: 4096,
160
229
  kindString: 'Call signature',
161
230
  modifiers: docspecObject.modifiers ?? [],
162
231
  name: docspecObject.name,
163
232
  parameters: docspecObject.args
164
233
  ?.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
- })),
234
+ .map((arg) => {
235
+ const paramTypeDoc: TypeDocObject = {
236
+ comment: docstring.args?.[arg.name]
237
+ ? {
238
+ summary: [
239
+ {
240
+ kind: 'text',
241
+ text: docstring.args[arg.name],
242
+ },
243
+ ],
244
+ }
245
+ : undefined,
246
+ defaultValue: arg.default_value as string,
247
+ flags: {
248
+ isOptional: arg.datatype?.includes('Optional') || arg.default_value !== undefined,
249
+ 'keyword-only': arg.type === 'KEYWORD_ONLY',
250
+ },
251
+ id: this.symbolIdTracker.getNewId(),
252
+ kind: 32_768,
253
+ kindString: 'Parameter',
254
+ name: arg.name,
255
+ type: this.pythonTypeResolver.registerType(arg.datatype),
256
+ }
257
+
258
+ this.symbolIdTracker.addNewReference(paramTypeDoc);
259
+
260
+ return paramTypeDoc;
261
+ }),
187
262
  type: this.pythonTypeResolver.registerType(docspecObject.return_type),
188
263
  };
264
+
265
+ this.symbolIdTracker.addNewReference(methodTypeDoc);
266
+
267
+ return methodTypeDoc;
189
268
  }
190
269
 
191
270
  /**
@@ -200,6 +279,10 @@ export class DocspecTransformer {
200
279
  parentTypeDoc,
201
280
  moduleName,
202
281
  }: TransformObjectOptions) {
282
+ if (currentDocspecNode?.datatype === 'TypeAlias') {
283
+ this.pythonTypeResolver.registerType(currentDocspecNode.value as string, currentDocspecNode.name);
284
+ }
285
+
203
286
  if (isHidden(currentDocspecNode)) {
204
287
  for (const docspecMember of currentDocspecNode.members ?? []) {
205
288
  this.walkAndTransform({
@@ -225,12 +308,7 @@ export class DocspecTransformer {
225
308
  return;
226
309
  }
227
310
 
228
- const currentId = getOID();
229
-
230
- this.symbolIdMap[currentId] = {
231
- qualifiedName: currentDocspecNode.name,
232
- sourceFileName: filePathInRepo,
233
- };
311
+ const currentId = this.symbolIdTracker.getNewId();
234
312
 
235
313
  // Get the module name of the member, and check if it has a shortcut (reexport from an ancestor module)
236
314
  const fullName = `${moduleName}.${currentDocspecNode.name}`;
@@ -273,6 +351,8 @@ export class DocspecTransformer {
273
351
  type: typedocType,
274
352
  };
275
353
 
354
+ this.symbolIdTracker.addNewReference(currentTypedocNode);
355
+
276
356
  if (currentTypedocNode.kindString === 'Method') {
277
357
  currentTypedocNode.signatures = [
278
358
  this.makeMethodSignature(currentDocspecNode, currentDocspecNode.parsedDocstring),
@@ -1,3 +1,4 @@
1
+ /* eslint-disable */
1
2
  import childProcess from 'child_process';
2
3
  import fs from 'fs';
3
4
  import path from 'path';
@@ -17,17 +18,23 @@ const PYTHON_SCRIPT_FILEPATH = path.join(__dirname, '..', '..', '..', '..', 'pyt
17
18
  export class PythonTypeResolver {
18
19
  private typedocTypes: TypeDocType[] = [];
19
20
 
21
+ private aliases: Record<string, string> = {};
22
+
20
23
  /**
21
24
  * Register a new Python type to be resolved.
22
25
  *
23
26
  * Given a string representation of the type, returns a Typedoc type object.
24
27
  */
25
- registerType(docspecType?: DocspecType): TypeDocType {
28
+ registerType(docspecType?: DocspecType, aliasName?: string): TypeDocType {
26
29
  const newType: TypeDocType = {
27
30
  name: docspecType?.replaceAll(/#.*/g, '').replaceAll('\n', '').trim() ?? 'Undefined',
28
31
  type: 'reference',
29
32
  };
30
33
 
34
+ if (aliasName) {
35
+ this.aliases[aliasName] = newType.name;
36
+ }
37
+
31
38
  this.typedocTypes.push(newType);
32
39
  return newType;
33
40
  }
@@ -64,9 +71,13 @@ export class PythonTypeResolver {
64
71
  TypeDocType
65
72
  >;
66
73
 
74
+ this.applyAliases(parsedTypes);
75
+
67
76
  for (const originalType of this.typedocTypes) {
68
77
  if (originalType.type === 'reference') {
69
- const parsedType = parsedTypes[originalType.name];
78
+ // The verbatim name of the type will always be in `parsedTypes`.
79
+ // If it references a `TypeAlias`, it should have a priority.
80
+ const parsedType = parsedTypes[this.aliases[originalType.name]] ?? parsedTypes[originalType.name];
70
81
 
71
82
  if (parsedType) {
72
83
  for (const key of Object.keys(parsedType)) {
@@ -85,4 +96,15 @@ export class PythonTypeResolver {
85
96
  getBaseType(type: string): string {
86
97
  return type?.replace(/Optional\[(.*)]/g, '$1').split('[')[0];
87
98
  }
99
+
100
+ private applyAliases(obj: Record<string, Record<string, any> | { name: string }>) {
101
+ for (const key of Object.keys(obj)) {
102
+ if (obj[key]?.name && this.aliases[obj[key]?.name]) {
103
+ obj[key].name = this.aliases[obj[key]?.name];
104
+ }
105
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
106
+ this.applyAliases(obj[key] as any);
107
+ }
108
+ }
109
+ }
88
110
  }
@@ -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.