Skip to content

Instantly share code, notes, and snippets.

@donmccurdy
Last active September 13, 2023 20:50
Show Gist options
  • Save donmccurdy/db75b86a28a775135e33317ce2694a32 to your computer and use it in GitHub Desktop.
Save donmccurdy/db75b86a28a775135e33317ce2694a32 to your computer and use it in GitHub Desktop.
RFC: Adopting JSDoc in three.js

RFC: Adopting JSDoc in three.js

Summary

Request for Comment (RFC) and a proposal for adopting JSDoc within the three.js project. The proposal intends to mitigate or solve two longstanding problems:

  1. Unofficial type definitions: Increasingly, many web projects and IDEs rely on type definitions in libraries, which three.js does not natively provide. The community maintains type definitions in a separate repository, with some overhead in cost and potential for mistakes or delays.
  2. Cost of maintaining localized documentation: API documentation for each class is maintained in hand-written HTML files, one file for each {class, language} pair. Maintainers contribute to the English-language documentation, while volunteers are largely responsible for identifying where English translations have changed and updating the localized versions.

Milestone 1: JSDoc Type Definitions

Required if RFC is accepted.

JavaScript source files will be annotated with comments in JSDoc format, describing only those type definitions required for public-facing APIs. Human-readable API descriptions are not present at this milestone. From the JSDoc comments, release builds will generate TypeScript type definitions published to npm within the library.

Example: `src/core/Object.js`
/**
 * @property {true} isObject3D
 * @property {string} uuid
 * @property {number} id
 * @property {string} name
 * @property {string} type
 * @property {Object3D|null} parent
 * @property {Object3D[]} children
 * @property {Vector3} up
 * @property {Vector3} position
 * @property {Euler} rotation
 * @property {Vector3} scale
 * ...
 */
class Object3D extends EventDispatcher {

	constructor() { ... }
	
	/**
	 * @param {WebGLRenderer} renderer
	 * @param {Scene} scene
	 * @param {Camera} camera
	 * @param {BufferGeometry} geometry
	 * @param {Material} material
	 * @param {any} group
	 */
	onBeforeRender() { ... }
	
	/**
	 * @param {WebGLRenderer} renderer
	 * @param {Scene} scene
	 * @param {Camera} camera
	 * @param {BufferGeometry} geometry
	 * @param {Material} material
	 * @param {any} group
	 */
	onAfterRender() { ... }
	
	/**
	 * @param {Matrix4} matrix
	 */
	applyMatrix4(matrix) { ... }
 
  ...
}

We'll add typescript to package.json#devDependencies, and create a tsconfig.json file with checkJS and allowJS options enabled. Editors like VSCode should then automatically provide hints when types are invalid. The code will run as expected in a browser, regardless of whether types are updated or compiled. At build time, TypeScript compiles the JSDoc comments to build/three.d.ts, including API definitions for the entire library — including three and three/addons/*. See dts-buddy for details on how this should work.

Milestone 2: Generate English-language API Documentation

Optional, and depends on Milestone 1.

In addition to type annotations, JSDoc comments will now include human-readable descriptions, in English, for each public-facing API item. In a build step, tooling will process the JSDoc comments or the d.ts files, and generate corresponding HTML pages identical to our current pages.

What about inline examples?

If desired, examples may be specified in one of two ways:

/**
 * Description of Mesh class here.
 *
 * @example link_to_example.html
 *
 * @example
 * Inline example below:
 * ```javascript
 * import { Mesh } from 'three';
 * const mesh = new Mesh( geometry, material );
 * ```
 */
class Mesh {
}

The former requires external files, and the latter is more verbose. Either will require some work on the HTML generator, which I believe is manageable. For simplicity, it may be preferable to leave these examples out of the API documentation and link to other examples or the manual instead.

Milestone 3: Generate Translated API Documentation

Optional, and depends on Milestones 2–3.

JSDoc or type definitions are processed in a build step, assigning a unique key to each English-language comment (e.g. Mesh#geometry). For each target output language, a human readable file (likely .csv or .yaml) is generated with content equivalent to:

Example: lang_it.csv

key enUpdated itUpdated en it
Mesh#geometry 2021-05-04 2021-10-15 An instance of BufferGeometry... Un'istanza di BufferGeometry...
Mesh#material 2021-05-04 2021-10-15 An instance of Material... Un'istanza di Material...

The lastUpdated column will be maintained by the generator, allowing translators to more easily find sections where the English comments have changed since the last translation. Optionally, translations may be modified in external software like Microsoft Excel or Google Sheets.

In a final build step, these translation files are processed, and translated HTML pages for API documentation are generated alongside the English-language versions.

Diagram

Flow diagram of the process described by this RFC

Source: https://www.tldraw.com/v/2dwr_7vxMpiVC0TedXNoNZGe?viewport=-314%2C-339%2C1158%2C869&page=page%3AaiaUOhQsX2ckmNRcLpS4R

Disadvantages

  • By generating API documentation automatically, the process for maintaining API docs diverges from the process for maintaining the manual and other written resources on the website.
  • JSDoc type annotations are admittedly verbose, and less ergonomic to write than TypeScript annotations. This problem could be mitigated if proposals for JavaScript Type Annotations are approved and implemented.

Alternatives considered

  • Rewrite three.js in TypeScript. None of the primary maintainers of three.js are currently interested in a change toward writing and maintaining TypeScript code.

  • Maintain d.ts type definitions manually. Manually updating d.ts files was attempted as a trial for several three.js releases. Cost of maintenance was prohibitive, and inability to validate hand-written d.ts files against source files created unacceptable problems.

Possible blockers

@donmccurdy
Copy link
Author

donmccurdy commented Sep 12, 2023

The answers to 1–3 are not complicated, but they are frankly a distraction from the issues here. If we get to the point where this proposal is no longer blocked by known issues, and we have actual PRs or JSDoc changes to discuss, I'd be happy to answer these questions.

@pailhead In the interest of clarity, I'll be direct. It's my opinion that you have a history of taking up more of the available oxygen in three.js discussions than is professionally acceptable, or commensurate with your expertise and contributions. You appear to be repeating the pattern now in this thread. I've noted your concerns, and would ask that you step back and leave room for others to give feedback.

@pailhead
Copy link

Noted.

@Methuselah96
Copy link

Methuselah96 commented Sep 12, 2023

@donmccurdy Thanks for your thoughtful and patient responses. As noted above I'd be interested in helping in any way I can. Is there a good place we can chat without taking up space here? My Discord username is the same as my GitHub username.

I'm interested in discussing what next steps might look like (e.g., creating some draft PRs to explore different options, working on tooling, waiting on comment from other maintainers, etc.). as well as whether you're okay with me proposing a slightly modified RFC that details how .d.ts+JSDoc could solve the same root issues with different trade-offs. I'm still hopeful that a .d.ts+JSDoc solution might be appealing despite it being decided against in the past if it solves the problems you're trying to solve, in the same way you're hopeful about a JSDoc solution being appealing despite it also being decided against in the past.

@Methuselah96
Copy link

Methuselah96 commented Sep 13, 2023

I now remember you mentioning a desire to not discuss writing .d.ts by hand, sorry for bringing that up again. There are definitely advantages to the JSDoc being in the source file anyway, mainly it being more likely that it will get updated with source code changes.

The first example that comes to mind of types that we probably couldn't express in the same way in JSDoc are some of the EventDispatcher methods because they has overloads with different generic constraints. You mentioned tsd-jsdoc (looks potentially unmaintained), ts-morph, or dts-buddy as some potential tools to investigate. I'm interested in helping doing some investigation, but if it's too soon or you'd rather do it yourself, that's fine too.

@donmccurdy
Copy link
Author

Thanks @Methuselah96! Let's continue offline, I'll send over a message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment