Skip to content

Commit 7e37cd2

Browse files
committed
Initial Release - v1.0.0
0 parents  commit 7e37cd2

File tree

9 files changed

+307
-0
lines changed

9 files changed

+307
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
node_modules

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
This project adheres to [Semantic Versioning](https://semver.org) and follows the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
6+
7+
---
8+
9+
## [1.0.0] – 2025-07-06
10+
11+
### Added
12+
13+
- This marks the first release of **jsdoc-plugin-esnext**
14+
- Smart JSDoc plugin to support native ES2022+ class features like private fields, static members and arrow-bound methods.

CONTRIBUTING.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Contributing
2+
3+
You're very welcome to submit issues or suggest improvements! However, for the moment, code changes will only be accepted by the project maintainer.
4+
5+
## Project Maintainer
6+
7+
- [Frank Kudermann](https://github.com/alphanull) ([@alphanull](https://github.com/alphanull)) – Author & Maintainer
8+
9+
## How to Contribute?
10+
11+
Feel free to:
12+
13+
- Open issues with questions, suggestions, or bug reports.
14+
- Provide feedback and feature requests.
15+
16+
Thank you for your understanding! 😊

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright © 2024-present Frank Kudermann @ alphanull.de
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
![License](https://img.shields.io/github/license/alphanull/jsdoc-plugin-esnext)
2+
![Version](https://img.shields.io/npm/v/@alphanull/jsdoc-plugin-esnext)
3+
4+
# JSDoc ESNext Plugin
5+
6+
**Smart JSDoc plugin that adds full ES2022+ class-feature support – private fields, static members and arrow-bound methods.**
7+
8+
Modern JavaScript syntax isn’t always accurately recognized by JSDoc. This plugin enables accurate recognition of modern ECMAScript class structures and enhances the resulting documentation.
9+
10+
## Features
11+
12+
* Restores accurate naming for native `#privateFields` and `#methods()`.
13+
* Automatically tags private members with `@private`.
14+
* Detects `static` class members and applies `@static` and `scope: static`.
15+
* Treats arrow-bound methods as `@function`.
16+
* Detects assignments like `this.#foo = ...` in constructors.
17+
* Works seamlessly with JSDoc's default parser.
18+
* Works with all themes (but see note below!).
19+
* Perfectly integrates with [VisionTheme](https://github.com/alphanull/jsdoc-vision-theme) for a modern UI (optional).
20+
* Tested with JSDoc 3.6.11 and 4.0.4.
21+
22+
## Installation
23+
24+
```bash
25+
npm install --save-dev @alphanull/jsdoc-plugin-esnext
26+
```
27+
28+
Then, add the plugin to your JSDoc configuration file:
29+
30+
```json
31+
{
32+
"plugins": [
33+
"@alphanull/jsdoc-plugin-esnext"
34+
]
35+
}
36+
```
37+
38+
## Example
39+
40+
```js
41+
export default class Example {
42+
43+
/**
44+
* Static counter
45+
* @type {number}
46+
*/
47+
static counter = 0;
48+
49+
/**
50+
* Private field
51+
* @type {string}
52+
*/
53+
static #secret = 'abc';
54+
55+
/**
56+
* Bound handler
57+
* @function
58+
*/
59+
#handleClick = () => {
60+
console.log('clicked');
61+
}
62+
63+
constructor() {
64+
this.#local = 123;
65+
}
66+
67+
/**
68+
* Private method
69+
*/
70+
#compute() {
71+
return true;
72+
}
73+
}
74+
```
75+
76+
### Resulting Documentation:
77+
78+
| ![before](assets/before.jpg) | ![after](assets/after.jpg) |
79+
| :----------------------------------------------------------: | :----------------------------------------------------------: |
80+
| **Without plugin** | **Using jsdoc-plugin-esnext** |
81+
82+
* `counter` appears with `@static` and type.
83+
* `#secret` is listed as `private static`.
84+
* `#handleClick` is listed as a private function.
85+
* `#compute()` is listed as a private method.
86+
87+
## Limitations
88+
89+
While there are no limitations with this plugin per se, for private members (which start with "#") there can be resulting hash links containing two hashes, like: `<a href="##privateMember">#privateMember</a>` which can lead to broken links. Unfortunately, this cannot be handled by the plugin itself and needs to be managed by the theme.
90+
91+
The good news is that documenting modern code typically requires a modern theme anyway. Consider using the brand-new [JSDoc VisionTheme](https://github.com/alphanull/jsdoc-vision-theme), which addresses this issue and is fully tested and optimized for compatibility with this plugin.
92+
93+
## License
94+
95+
[MIT License](https://opensource.org/license/MIT)
96+
97+
Copyright © 2025–present Frank Kudermann @ [alphanull.de](https://alphanull.de)

assets/after.jpg

74.4 KB
Loading

assets/before.jpg

73.8 KB
Loading

index.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* JSDoc plugin that recovers and standardizes documentation of ECMAScript-next features:
3+
* - Native private methods (#method())
4+
* - Native private properties (#property)
5+
* - Bound arrow functions (#method = () => {})
6+
* It injects missing @function and @private tags where necessary and formats the resulting doclets consistently.
7+
* @author Frank Kudermann @ alphanull
8+
* @version 1.0.0
9+
* @license MIT
10+
*/
11+
export const handlers = {
12+
13+
/**
14+
* Triggered when a symbol (e.g. Method or field) is found during AST traversal.
15+
* Used to recover names for private class members and inline assignments to `this.#foo`.
16+
* @param {Object} e The event containing symbol data and AST node information.
17+
*/
18+
symbolFound(e) {
19+
20+
// Phase 1: Detect native private methods defined via #method() syntax
21+
if (e.code?.type === 'MethodDefinition'
22+
&& e.code?.node?.key?.type === 'PrivateName'
23+
&& e.code?.node?.key?.id?.name) {
24+
25+
e.code.name = e.code.node.key.id.name;
26+
}
27+
28+
// Phase 2: Detect assignments to this.#property inside constructor or methods
29+
if (e.code?.name === 'this.' // <- smells like a jsdoc parse bug
30+
&& e.astnode?.type === 'AssignmentExpression'
31+
&& e.astnode?.left?.type === 'MemberExpression'
32+
&& e.astnode?.left?.property?.type === 'PrivateName'
33+
&& e.astnode?.left?.property?.id?.name) {
34+
35+
e.code.name = e.astnode.left.property.id.name;
36+
}
37+
},
38+
39+
/**
40+
* Triggered after all doclets have been collected. Used to finalize name formatting,
41+
* unify access levels, and merge scattered information into clean private member entries.
42+
* @param {Object} context The parse context object.
43+
* @param {Array<Object>} context.doclets The list of all generated doclets.
44+
*/
45+
parseComplete({ doclets }) {
46+
47+
const privatePropNames = new Set();
48+
49+
// Phase 1: Collect names of undocumented ClassPrivateProperty placeholders
50+
for (const doclet of doclets) {
51+
if (doclet.undocumented && doclet.meta?.code?.type === 'ClassPrivateProperty') {
52+
privatePropNames.add(doclet.name);
53+
}
54+
}
55+
56+
// Phase 2: Apply corrections for all relevant doclets
57+
for (const doclet of doclets) {
58+
59+
// Handle value-carrying assignment doclets related to private properties
60+
if (doclet.kind === 'member'
61+
&& doclet.scope === 'inner'
62+
&& typeof doclet.name === 'string'
63+
&& privatePropNames.has(doclet.name)) {
64+
65+
doclet.name = `#${doclet.name}`;
66+
doclet.longname = `${doclet.memberof}#${doclet.name}`;
67+
doclet.access = 'private';
68+
doclet.scope = 'instance';
69+
// Do NOT reset undocumented = false here, placeholder doclets must remain hidden
70+
}
71+
72+
// correct arrow methods by fixing the kind
73+
74+
const isArrowMethod = doclet.kind === 'member'
75+
&& (doclet.meta?.code?.type === 'ArrowFunctionExpression'
76+
|| doclet.meta?.code?.node?.value?.type === 'ArrowFunctionExpression');
77+
78+
if (isArrowMethod) {
79+
doclet.kind = 'function';
80+
}
81+
82+
const isPrivateMethod = doclet.meta?.code?.node?.type === 'MethodDefinition'
83+
&& doclet.meta?.code?.node?.key?.type === 'PrivateName';
84+
85+
const isPrivateProperty = doclet.meta?.code?.type === 'ClassPrivateProperty'
86+
|| doclet.meta?.code?.node?.type === 'PropertyDefinition'
87+
&& doclet.meta?.code?.node?.key?.type === 'PrivateName';
88+
89+
if (isPrivateMethod || isPrivateProperty) {
90+
91+
// Common logic for private methods and properties with recognizable AST nodes
92+
93+
if (!doclet.name.startsWith('#')) {
94+
doclet.name = `#${doclet.name}`;
95+
}
96+
97+
if (!doclet.longname || doclet.longname === doclet.memberof) {
98+
doclet.longname = `${doclet.memberof}#${doclet.name}`;
99+
}
100+
101+
doclet.access = 'private';
102+
}
103+
104+
if (doclet.meta?.code?.node?.static === true) {
105+
doclet.scope = 'static';
106+
}
107+
}
108+
}
109+
};

package.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@alphanull/jsdoc-plugin-esnext",
3+
"version": "1.0.0",
4+
"license": "MIT",
5+
"description": "Smart JSDoc plugin that adds full ES2022+ class-feature support – private fields, static members and arrow-bound methods.",
6+
"keywords": [
7+
"jsdoc",
8+
"plugin",
9+
"jsdoc-plugin",
10+
"private fields",
11+
"static",
12+
"arrow functions",
13+
"esnext",
14+
"es2022",
15+
"documentation"
16+
],
17+
"author": {
18+
"name": "Frank Kudermann",
19+
"email": "kudermann@alphanull.de",
20+
"url": "https://alphanull.de"
21+
},
22+
"repository": {
23+
"type": "git",
24+
"url": "git+https://github.com/alphanull/jsdoc-plugin-esnext.git"
25+
},
26+
"homepage": "https://github.com/alphanull/jsdoc-plugin-esnext#readme",
27+
"bugs": {
28+
"url": "https://github.com/alphanull/jsdoc-plugin-esnext/issues"
29+
},
30+
"type": "module",
31+
"main": "index.js",
32+
"exports": {
33+
".": "./index.js"
34+
},
35+
"files": [
36+
"assets",
37+
"index.js",
38+
"README.md",
39+
"LICENSE"
40+
],
41+
"publishConfig": {
42+
"access": "public"
43+
},
44+
"sideEffects": false,
45+
"engines": {
46+
"node": ">=16.0.0"
47+
}
48+
}

0 commit comments

Comments
 (0)