|
| 1 | +- Start Date: 2020-12-21 |
| 2 | +- RFC PR: (leave this empty) |
| 3 | +- Kibana Issue: (leave this empty) |
| 4 | + |
| 5 | +# Summary |
| 6 | + |
| 7 | +Automatically generate API documentation for every plugin that exposes a public API within Kibana. |
| 8 | +This does not cover REST API docs, but is targetted towards our javascript |
| 9 | +plugin APIs. |
| 10 | + |
| 11 | +# Screenshots |
| 12 | + |
| 13 | +TODO when I have better screenshots. |
| 14 | + |
| 15 | +# Architecture design |
| 16 | + |
| 17 | +## Hosting |
| 18 | + |
| 19 | +In the short term, the generated docs will reside inside the kibana repo, inside a top level `api_docs` folder. In the long term, we could investigate having the docs system run a script to generated the mdx files, so we don’t need to store them inside the repo. |
| 20 | + |
| 21 | +They will be hosted online wherever the new docs system ends up. This can temporarily be accessed at https://elasticdocstest.netlify.app/docs/. |
| 22 | + |
| 23 | +## Overview |
| 24 | + |
| 25 | +The first stage is to collect the list of plugins using the existing `findPlugins` logic, with [some new fields](#proposed-plugin-changes). |
| 26 | + |
| 27 | +For every plugin, the initial list of api nodes are collected from three "scopes": |
| 28 | + - plugin/public/index.ts |
| 29 | + - plugin/server/index.ts |
| 30 | + - plugin/common/index.ts |
| 31 | + |
| 32 | +Every node in each list is then recursively transformed into a json structure that represents a plugin's public API: |
| 33 | + |
| 34 | +```ts |
| 35 | + |
| 36 | +/** |
| 37 | + * Contains all the information neccessary to build API docs for this particular plugin. |
| 38 | + */ |
| 39 | +export interface PluginApiDef { |
| 40 | + id: string; |
| 41 | + serviceFolders?: readonly string[]; |
| 42 | + public: ApiDocDef[]; |
| 43 | + server: ApiDocDef[]; |
| 44 | + common: ApiDocDef[]; |
| 45 | +} |
| 46 | + |
| 47 | +/** |
| 48 | + * Contains all the information neccessary to render a single API inside the docs system. |
| 49 | + */ |
| 50 | +export interface ApiDocDef { |
| 51 | + /** |
| 52 | + * Used to create an anchor link to this API. |
| 53 | + */ |
| 54 | + id?: string; |
| 55 | + |
| 56 | + /** |
| 57 | + * The name of the api. |
| 58 | + */ |
| 59 | + label: string; |
| 60 | + |
| 61 | + /** |
| 62 | + * The kind of type this API represents, e.g. string, number, Object, Interface, Class. |
| 63 | + */ |
| 64 | + type: TypeKind; |
| 65 | + |
| 66 | + /** |
| 67 | + * Certain types have children. For instance classes have class members, functions will list |
| 68 | + * their parameters here, classes will list their class members here, and objects and interfaces |
| 69 | + * will list their properties. The elastic-docs system can use the `type` to potentially render |
| 70 | + * these children differently. |
| 71 | + */ |
| 72 | + children?: ApiDocDef[]; |
| 73 | + |
| 74 | + /** |
| 75 | + * Api node comment. |
| 76 | + */ |
| 77 | + description?: TextWithLinks; |
| 78 | + |
| 79 | + /** |
| 80 | + * If the type is a function, it's signature should be displayed. Currently this overlaps with type |
| 81 | + * sometimes, and will sometimes be left empty for large types (like classes and interfaces). |
| 82 | + */ |
| 83 | + signature?: TextWithLinks; |
| 84 | + |
| 85 | + // Relevant for functions with @returns comments. |
| 86 | + returnComment?: TextWithLinks; |
| 87 | + |
| 88 | + // Will contain the tags on a comment, like `beta` or `deprecated`. |
| 89 | + // Won't include param or returns tags. |
| 90 | + tags?: string[]; |
| 91 | + |
| 92 | + // Every plugn that exposes functionality from their setup and start contract |
| 93 | + // should have a single exported type for each. These get pulled to the top because |
| 94 | + // they are accessed differently than other exported functionality and types. |
| 95 | + lifecycle?: Lifecycle; |
| 96 | + |
| 97 | + /** |
| 98 | + * Used to create links to github to view the code for this API. |
| 99 | + */ |
| 100 | + source: { |
| 101 | + path: string; |
| 102 | + lineNumber: number; |
| 103 | + }; |
| 104 | +} |
| 105 | + |
| 106 | +enum TypeKind { |
| 107 | + ClassKind = 'Class', |
| 108 | + FunctionKind = 'Function', |
| 109 | + VariableKind = 'Variable', |
| 110 | + ObjectKind = 'Object', |
| 111 | + InterfaceKind = 'Interface', |
| 112 | + TypeKind = 'Type', |
| 113 | + Unknown = 'Unknown', |
| 114 | + Parameter = 'Parameter', |
| 115 | + Property = 'Property', |
| 116 | + String = 'string', |
| 117 | + Number = 'number', |
| 118 | + Boolean = 'boolean', |
| 119 | + Array = 'Array', |
| 120 | +} |
| 121 | + |
| 122 | +/** |
| 123 | + * |
| 124 | + */ |
| 125 | +type TextWithLinks = string | Array<string | Reference>; |
| 126 | + |
| 127 | +interface Reference { |
| 128 | + docId: string; |
| 129 | + section: string; |
| 130 | + text: string; |
| 131 | +} |
| 132 | +``` |
| 133 | + |
| 134 | +## Technology: ts-morph vs api-extractor |
| 135 | + |
| 136 | +[Api-extractor](https://api-extractor.com/) is a utility built from microsoft that parses typescript code into json files that can then be used in a custom [api-documenter](https://api-extractor.com/pages/setup/generating_docs/) in order to build documentation. This is what we [have now](https://github.com/elastic/kibana/tree/master/docs/development), except we use the default api-documenter. Unfortunately, because our plugins aren’t separate packages, and the way api-extractor works, we can’t build cross plugin links this way. |
| 137 | + |
| 138 | +[ts-morph](https://github.com/dsherret/ts-morph) is a utility built and maintained by a single person, which sits a layer above the raw typescript compiler. It affords greater flexibility, thus supports cross plugin links (among other things like links to source files). The downsides of using this library are: |
| 139 | + |
| 140 | + - Risks of relying on a package maintained by a single developer |
| 141 | + - Less re-usability across repositories. What if EUI wanted to use the same system? |
| 142 | + |
| 143 | +I recommend we move ahead with ts-morph, acknowleding the possibility of migrating to api-extractor in the future. If so, the effort shouldn’t be a large one. |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | +# Drawbacks |
| 148 | + |
| 149 | +Why should we *not* do this? Please consider: |
| 150 | + |
| 151 | +- implementation cost, both in term of code size and complexity |
| 152 | +- the impact on teaching people Kibana development |
| 153 | +- integration of this feature with other existing and planned features |
| 154 | +- cost of migrating existing Kibana plugins (is it a breaking change?) |
| 155 | + |
| 156 | +There are tradeoffs to choosing any path. Attempt to identify them here. |
| 157 | + |
| 158 | +# Alternatives |
| 159 | + |
| 160 | +What other designs have been considered? What is the impact of not doing this? |
| 161 | + |
| 162 | +# Adoption strategy |
| 163 | + |
| 164 | +If we implement this proposal, how will existing Kibana developers adopt it? Is |
| 165 | +this a breaking change? Can we write a codemod? Should we coordinate with |
| 166 | +other projects or libraries? |
| 167 | + |
| 168 | +# How we teach this |
| 169 | + |
| 170 | +What names and terminology work best for these concepts and why? How is this |
| 171 | +idea best presented? As a continuation of existing Kibana patterns? |
| 172 | + |
| 173 | +Would the acceptance of this proposal mean the Kibana documentation must be |
| 174 | +re-organized or altered? Does it change how Kibana is taught to new developers |
| 175 | +at any level? |
| 176 | + |
| 177 | +How should this feature be taught to existing Kibana developers? |
| 178 | + |
| 179 | +# Unresolved questions |
| 180 | + |
| 181 | +## REST API |
| 182 | + |
| 183 | +This RFC does not cover REST API documentation, though it worth considering where |
| 184 | +REST APIs registered by plugins should go in the docs. |
0 commit comments