1
+ module td . converter
2
+ {
3
+ /**
4
+ * This plugin allows an ES6 module to specify its TypeDoc name.
5
+ * It also allows multiple ES6 modules to be merged together into a single TypeDoc module.
6
+ *
7
+ * @usage
8
+ * At the top of an ES6 module, add a "dynamic module comment". Insert "@module typedocModuleName" to
9
+ * specify that this ES6 module should be merged with module: "typedocModuleName".
10
+ *
11
+ * Similar to the [[DynamicModulePlugin]], ensure that there is a comment tag (even blank) for the
12
+ * first symbol in the file.
13
+ *
14
+ * @example
15
+ * ```
16
+ *
17
+ * /**
18
+ * * @module newModuleName
19
+ * */
20
+ * /** for typedoc /
21
+ * import {foo} from "../foo";
22
+ * export let bar = "bar";
23
+ * ```
24
+ *
25
+ * Also similar to [[DynamicModulePlugin]], if @preferred is found in a dynamic module comment, the comment
26
+ * will be used as the module comment, and documentation will be generated from it (note: this plugin does not
27
+ * attempt to count lengths of merged module comments in order to guess the best one)
28
+ */
29
+ export class ModuleAnnotationPlugin extends ConverterPlugin
30
+ {
31
+ /** List of module reflections which are models to rename */
32
+ private moduleRenames : ModuleRename [ ] ;
33
+
34
+ constructor ( converter :Converter ) {
35
+ super ( converter ) ;
36
+ converter . on ( Converter . EVENT_BEGIN , this . onBegin , this ) ;
37
+ converter . on ( Converter . EVENT_CREATE_DECLARATION , this . onDeclaration , this ) ;
38
+ converter . on ( Converter . EVENT_RESOLVE_BEGIN , this . onBeginResolve , this ) ;
39
+ }
40
+
41
+ /**
42
+ * Triggered when the converter begins converting a project.
43
+ *
44
+ * @param context The context object describing the current state the converter is in.
45
+ */
46
+ private onBegin ( context :Context ) {
47
+ this . moduleRenames = [ ] ;
48
+ }
49
+
50
+ /**
51
+ * Triggered when the converter has created a declaration reflection.
52
+ *
53
+ * @param context The context object describing the current state the converter is in.
54
+ * @param reflection The reflection that is currently processed.
55
+ * @param node The node that is currently processed if available.
56
+ */
57
+ private onDeclaration ( context :Context , reflection :models . Reflection , node ?:ts . Node ) {
58
+ if ( reflection . kindOf ( models . ReflectionKind . ExternalModule ) ) {
59
+ let comment = CommentPlugin . getComment ( node ) ;
60
+ // Look for @module
61
+ let match = / @ m o d u l e \s + ( \w + ) / . exec ( comment ) ;
62
+ if ( match ) {
63
+ // Look for @preferred
64
+ let preferred = / @ p r e f e r r e d / . exec ( comment ) ;
65
+ // Set up a list of renames operations to perform when the resolve phase starts
66
+ this . moduleRenames . push ( {
67
+ renameTo : match [ 1 ] ,
68
+ preferred : preferred != null ,
69
+ reflection : < models . ContainerReflection > reflection
70
+ } ) ;
71
+ }
72
+ }
73
+ }
74
+
75
+
76
+ /**
77
+ * Triggered when the converter begins resolving a project.
78
+ *
79
+ * @param context The context object describing the current state the converter is in.
80
+ */
81
+ private onBeginResolve ( context :Context ) {
82
+ let projRefs = context . project . reflections ;
83
+ let refsArray : models . Reflection [ ] = Object . keys ( projRefs ) . reduce ( ( m , k ) => { m . push ( projRefs [ k ] ) ; return m ; } , [ ] ) ;
84
+
85
+ // Process each rename
86
+ this . moduleRenames . forEach ( item => {
87
+ let renaming = < models . ContainerReflection > item . reflection ;
88
+ // Find an existing module that already has the "rename to" name. Use it as the merge target.
89
+ let mergeTarget = < models . ContainerReflection >
90
+ refsArray . filter ( ref => ref . kind === renaming . kind && ref . name === item . renameTo ) [ 0 ] ;
91
+
92
+ // If there wasn't a merge target, just change the name of the current module and exit.
93
+ if ( ! mergeTarget ) {
94
+ renaming . name = item . renameTo ;
95
+ return ;
96
+ }
97
+
98
+ // Since there is a merge target, relocate all the renaming module's children to the mergeTarget.
99
+ let childrenOfRenamed = refsArray . filter ( ref => ref . parent === renaming ) ;
100
+ childrenOfRenamed . forEach ( ( ref : models . Reflection ) => {
101
+ // update links in both directions
102
+ ref . parent = mergeTarget ;
103
+ mergeTarget . children . push ( < any > ref )
104
+ } ) ;
105
+
106
+ // If @preferred was found on the current item, update the mergeTarget's comment
107
+ // with comment from the renaming module
108
+ if ( item . preferred )
109
+ mergeTarget . comment = renaming . comment ;
110
+
111
+ // Now that all the children have been relocated to the mergeTarget, delete the empty module
112
+ // Make sure the module being renamed doesn't have children, or they will be deleted
113
+ if ( renaming . children )
114
+ renaming . children . length = 0 ;
115
+ CommentPlugin . removeReflection ( context . project , renaming ) ;
116
+
117
+ // Remove @module and @preferred from the comment, if found.
118
+ CommentPlugin . removeTags ( mergeTarget . comment , "module" ) ;
119
+ CommentPlugin . removeTags ( mergeTarget . comment , "preferred" ) ;
120
+ } ) ;
121
+ }
122
+ }
123
+
124
+
125
+ /**
126
+ * Register this handler.
127
+ */
128
+ Converter . registerPlugin ( 'moduleAnnotation' , ModuleAnnotationPlugin ) ;
129
+
130
+ interface ModuleRename {
131
+ renameTo : string ;
132
+ preferred : boolean ;
133
+ reflection : models . ContainerReflection ;
134
+ }
135
+ }
0 commit comments