cesium/Documentation/Contributors/DocumentationGuide/README.md

574 lines
15 KiB
Markdown

# Documentation Guide
CesiumJS's reference documentation is one of the most popular sections of the CesiumJS website, and a critical resource for developers.
This guide describes best practices for writing reference doc.
Always include doc for new identifiers (classes, functions, properties, constants) in the public CesiumJS API.
Generally, just follow the patterns that are already in comparable parts of the code, e.g., if you are documenting a new utility function in `Core`, look at a function in `Core` such as [`binarySearch`](https://github.com/CesiumGS/cesium/blob/main/Source/Core/binarySearch.js); likewise, if you are documenting a new class in `Scene`, look at a similar class such as [`Model`](https://github.com/CesiumGS/cesium/blob/main/Source/Scene/Model.js).
- [Documentation Guide](#documentation-guide)
- [Building the Doc](#building-the-doc)
- [Basics](#basics)
- [Parameters](#parameters)
- [`options` Parameters](#options-parameters)
- [Exceptions](#exceptions)
- [Examples](#examples)
- [References](#references)
- [Classes](#classes)
- [Properties and Constants](#properties-and-constants)
- [Functions and Callbacks](#functions-and-callbacks)
- [Private](#private)
- [Layout Reference](#layout-reference)
- [Constructor Function](#constructor-function)
- [Member Function](#member-function)
- [Property](#property)
- [Property Getter/Setter](#property-gettersetter)
- [Standalone Function](#standalone-function)
- [TypeScript](#typescript)
## Building the Doc
The reference doc is written in JavaScript code comments using [JSDoc3](https://jsdoc.app/) tags. At the command line, build the doc from the root CesiumJS directory by running the following:
```bash
npm run build-docs
```
This creates a `Build/Documentation` directory with the built HTML files.
> Alternatively, you can build documentation in watch mode `npm run build-docs-watch` and have it generated automatically when source files change.
There is a link to the doc from CesiumJS's main `index.html` when running
```bash
npm start
```
![Documentation](toc.jpg)
## Basics
Consider one of the simplest functions in CesiumJS, [`defined`](../../../packages/engine/Source/Core/defined.js):
```javascript
/**
* @function
*
* @param {*} value The object.
* @returns {boolean} Returns true if the object is defined, returns false otherwise.
*
* @example
* if (Cesium.defined(positions)) {
* doSomething();
* } else {
* doSomethingElse();
* }
*/
function defined(value) {
return value !== undefined && value !== null;
}
```
- The doc for `defined` is in the comment starting with `/**`. JSDoc tags begin with `@`.
- `@function` tells JSDoc that this is a function.
- `@param` describes the function's parameters, and `@returns` describes the function's return value.
- `@example` describes a code sample.
The above reference doc is built into the following:
![Documentation for defined](defined.jpg)
This guide describes best practices for writing doc. For complete details on JSDoc tags, see their [documentation](https://jsdoc.app/).
## Parameters
- Document all function parameters.
- Use `[]` for optional parameters and include the default value, e.g.,
```javascript
* @param {number} [startingIndex=0] The index into the array at which to start packing the elements.
```
- Omit the default value if it is `undefined`, e.g.,
```javascript
* @param {Cartesian3} [result] The object on which to store the result.
```
- If a parameter can be more than one type, use `|` to separate the types, e.g.,
```javascript
* @param {GeometryInstance[]|GeometryInstance} [options.geometryInstances] The geometry instances - or a single geometry instance - to render.
```
As a complete example,
```javascript
/**
* Computes a Matrix4 instance from a Matrix3 representing the rotation
* and a Cartesian3 representing the translation.
*
* @param {Matrix3} rotation The upper left portion of the matrix representing the rotation.
* @param {Cartesian3} [translation=Cartesian3.ZERO] The upper right portion of the matrix representing the translation.
* @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created.
* @returns {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided.
*/
Matrix4.fromRotationTranslation = function (rotation, translation, result) {
// ..
```
generates
![Function Documentation](fromRotationTranslation.jpg)
The CesiumJS classes in the `Type` column are links to their doc.
## `options` Parameters
Each property of an `options` parameter (see the [Coding Guide](https://github.com/CesiumGS/cesium/true/main/Documentation/Contributors/CodingGuide/README.md#options-parameters)) should be documented with a separate `@param` tag, e.g.,
```javascript
* @param {object} [options] Object with the following properties:
* @param {number} [options.length=10000000.0] The length of the axes in meters.
* @param {number} [options.width=2.0] The width of the axes in pixels.
* @param {Matrix4} [options.modelMatrix=Matrix4.IDENTITY] The 4x4 matrix that defines the reference frame, i.e., origin plus axes, to visualize.
* @param {boolean} [options.show=true] Determines if this primitive will be shown.
* @param {object} [options.id] A user-defined object to return when the instance is picked with {@link Scene#pick}
```
generates
![Constructor Documentation](DebugModelMatrixPrimitive.jpg)
If all `options` properties are optional, also mark the `options` object optional.
## Exceptions
- Document exceptions after the `@param`/`@returns` section using `@exception`, e.g.,
```javascript
/**
* ...
*
* @exception {DeveloperError} aspectRatio must be greater than zero.
*/
Matrix4.computePerspectiveFieldOfView = function(fovY, aspectRatio, near, far, result) {
//>>includeStart('debug', pragmas.debug);
if (aspectRatio <= 0.0) {
throw new DeveloperError('aspectRatio must be greater than zero.');
}
// ...
```
- Do not document exceptions for missing parameters; it is implicit in the `@param` tag because the parameter is not optional, e.g.,
```javascript
/**
* Computes a Matrix4 instance from a column-major order array.
*
* @param {number[]} values The column-major order array.
* @param {Matrix4} [result] The object in which the result will be stored. If undefined a new instance will be created.
* @returns {Matrix4} The modified result parameter, or a new Matrix4 instance if one was not provided.
*/
Matrix4.fromColumnMajorArray = function (values, result) {
//>>includeStart('debug', pragmas.debug);
if (!defined(values)) {
throw new DeveloperError("values is required.");
}
//>>includeEnd('debug');
return Matrix4.clone(values, result);
};
```
## Examples
Developers almost always jump to an example before reading the doc. Provide concise but instructive code examples with enough context whenever possible.
Useful examples:
```javascript
/**
* ...
*
* @example
* const n = Cesium.Math.lerp(0.0, 2.0, 0.5); // returns 1.0
*/
CesiumMath.lerp = function(p, q, time) {
// ...
```
```javascript
/**
* ...
*
* @example
* // Apply non-uniform scale to node LOD3sp
* const node = model.getNode('LOD3sp');
* node.matrix = Cesium.Matrix4.fromScale(new Cesium.Cartesian3(5.0, 1.0, 1.0), node.matrix);
*/
Model.prototype.getNode = function(name) {
// ...
```
Unnecessary example:
```javascript
/**
* ..
*
* @example
* const f = Cesium.Math.EPSILON1;
*/
CesiumMath.EPSILON1 = 0.1;
```
- Use the Cesium namespace (`Cesium.`) in examples.
- Limit code in `@example` tags to 80 lines so it does not overflow.
## References
- Use `@see` sparingly to link to related classes, functions, and online resources., e.g.,
```javascript
/**
* Provides terrain or other geometry for the surface of an ellipsoid. The surface geometry is
* organized into a pyramid of tiles according to a {@link TilingScheme}. This type describes an
* interface and is not intended to be instantiated directly.
*
* @alias TerrainProvider
* @constructor
*
* @see EllipsoidTerrainProvider
* @see CesiumTerrainProvider
*/
function TerrainProvider() {
/* ... */
}
```
- Use `#` to reference an instance member (e.g., one that is assigned to the prototype); use `.` to access a static member, e.g.,
```javascript
@see Class
@see Class#instanceMember
@see Class.staticMember
```
- Use `{@link className}` to link to another documented type. This is not required for `@param` tags when the type is provided.
- Use `<code> </code>` tags when referring to parameters or other variable names and values within a description.
- Use `{@link URL|title}` to link to external sites.
## Classes
Define a class with `@alias` and `@constructor` tags on the constructor function, e.g.,
```javascript
/**
* A 3D Cartesian point.
*
* @alias Cartesian3
* @constructor
*
* ...
*/
function Cartesian3(x, y, z) {
// ...
```
## Properties and Constants
- Use `@type` and `@default` (whenever possible, except when the default is `undefined`) to document properties, e.g.,
```javascript
function Cartesian3(x, y) {
/**
* The X component.
*
* @type {number}
* @default 0.0
*/
this.x = x ?? 0.0;
// ...
```
- Use `@memberof` when documenting property getter/setters, e.g.,
```javascript
Object.defineProperties(Entity.prototype, {
/**
* Gets or sets whether this entity should be displayed. When set to true,
* the entity is only displayed if the parent entity's show property is also true.
*
* @memberof Entity.prototype
* @type {boolean}
*/
show: {
get: function() {
// ...
},
set: function(value) {
// ...
}
},
// ...
```
- Use `@readonly` to indicate read-only properties, e.g.,
```javascript
Object.defineProperties(Entity.prototype, {
/**
* Gets the unique ID associated with this object.
*
* @memberof Entity.prototype
* @type {string}
* @readonly
*/
id: {
get: function() {
return this._id;
}
},
// ...
```
- The description for readonly properties should start with "Gets", and the description for read/write properties should start with "Gets or sets."
- Document constants with `@constant`, e.g.,
```javascript
/**
* An immutable Cartesian3 instance initialized to (0.0, 0.0, 0.0).
*
* @type {Cartesian3}
* @constant
*/
Cartesian3.ZERO = Object.freeze(new Cartesian3(0.0, 0.0, 0.0));
```
## Functions and Callbacks
- Use `@function` when JSDoc can't infer that an identifier is a function because the JavaScript `function` keyword isn't used, e.g.,
```javascript
/**
* Creates a Cartesian4 from four consecutive elements in an array.
* @function
*
* @param {number[]} array The array whose four consecutive elements correspond to the x, y, z, and w components, respectively.
* @param {number} [startingIndex=0] The offset into the array of the first element, which corresponds to the x component.
* @param {Cartesian4} [result] The object on which to store the result.
* @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided.
*
* @example
* // Create a Cartesian4 with (1.0, 2.0, 3.0, 4.0)
* const v = [1.0, 2.0, 3.0, 4.0];
* const p = Cesium.Cartesian4.fromArray(v);
*
* // Create a Cartesian4 with (1.0, 2.0, 3.0, 4.0) using an offset into an array
* const v2 = [0.0, 0.0, 1.0, 2.0, 3.0, 4.0];
* const p2 = Cesium.Cartesian4.fromArray(v2, 2);
*/
Cartesian4.fromArray = Cartesian4.unpack;
```
- Use `@callback` to document a function signature, e.g.,
```javascript
/**
* Sort the items in the queue in-place.
*
* @param {Queue.Comparator} compareFunction A function that defines the sort order.
*/
Queue.prototype.sort = function (compareFunction) {
if (this._offset > 0) {
//compact array
this._array = this._array.slice(this._offset);
this._offset = 0;
}
this._array.sort(compareFunction);
};
/**
* A function used to compare two items while sorting a queue.
* @callback Queue.Comparator
*
* @param {*} a An item in the array.
* @param {*} b An item in the array.
* @returns {number} Returns a negative value if <code>a</code> is less than <code>b</code>,
* a positive value if <code>a</code> is greater than <code>b</code>, or
* 0 if <code>a</code> is equal to <code>b</code>.
*
* @example
* function compareNumbers(a, b) {
* return a - b;
* }
*/
```
## Private
Documentation is not generated for private members that start with `_`. It is often useful to still write doc comments for them for maintainability (see the [Coding Guide](https://github.com/CesiumGS/cesium/tree/main/Documentation/Contributors/CodingGuide/README.md#design)).
If a member or function doesn't start with `_`, but is intended to be private, use the `@private` tag at the bottom of the documentation, e.g.,
```javascript
/**
* A tween is an animation that interpolates the properties of two objects using an {@link EasingFunction}. Create
* one using {@link Scene#tweens} and {@link TweenCollection#add} and related add functions.
*
* @alias Tween
* @constructor
*
* @private
*/
function Tween(/* ... */) {
/* ... */
}
```
If no documentation comments are provided, the identifier will not be documented. In this case, `@private` is not strictly needed, but we use it anyway so it is clear that documentation was not forgotten, e.g.,
```javascript
/**
* @private
*/
function appendForwardSlash(url) {
/* ... */
}
```
Documentation for private elements can be generated by running the following:
```bash
npm run build-docs -- --private
```
## Layout Reference
There's a general flow to each documentation block that makes it easy to read. Tags are always in the same order with the same spacing.
### Constructor Function
```javascript
DESCRIPTION.
@alias NAME
@constructor
@param {TYPE} NAME DESCRIPTION.
@param {TYPE|OTHER_TYPE} NAME DESCRIPTION WITH LONG
WRAPPING LINES.
@exception {TYPE} DESCRIPTION.
@see TYPE
@see TYPE#INSTANCE_MEMBER
@see TYPE.STATIC_MEMBER
@example
[@private]
```
### Member Function
```javascript
DESCRIPTION.
@param {TYPE} NAME DESCRIPTION.
@param {TYPE|OTHER_TYPE} NAME DESCRIPTION WITH LONG
WRAPPING LINES.
@returns {TYPE} DESCRIPTION.
@exception {TYPE} DESCRIPTION.
@see TYPE
@see TYPE#INSTANCE_MEMBER
@see TYPE.STATIC_MEMBER
@example
[@private]
```
### Property
```javascript
DESCRIPTION.
@type {TYPE}
[@constant]
[@default DEFAULT_VALUE]
[@readonly]
@exception {TYPE} DESCRIPTION.
@see TYPE
@see TYPE#INSTANCE_MEMBER
@see TYPE.STATIC_MEMBER
@example
[@private]
```
### Property Getter/Setter
```javascript
DESCRIPTION.
@memberof CLASS_NAME.prototype
@type {TYPE}
[@default DEFAULT_VALUE]
[@readonly]
@exception {TYPE} DESCRIPTION.
@see TYPE
@see TYPE#INSTANCE_MEMBER
@see TYPE.STATIC_MEMBER
@example
[@private]
```
### Standalone Function
```javascript
DESCRIPTION.
@function
@param {TYPE} NAME DESCRIPTION.
@param {TYPE|OTHER_TYPE} NAME DESCRIPTION WITH LONG
WRAPPING LINES.
@returns {TYPE} DESCRIPTION.
@exception {TYPE} DESCRIPTION.
@see TYPE
@see TYPE#INSTANCE_MEMBER
@see TYPE.STATIC_MEMBER
@example
[@private]
```
## TypeScript
We also use JSDoc to build official TypeScript type definitions. Normally this behavior is transparent to the developer and happens as part of CI, however incorrect or non-standard JSDoc can lead to failures. If CI is failing because of the `build-ts` step, you can debug it locally by running:
```bash
npm run build-ts
```
In most cases, the TypeScript compiler will provide a very obvious error and line number which will help you track down the offending, most likely incorrect, JSDoc.