Skip to content

Commit c12a71c

Browse files
committed
Added various fixes for default exports, updated docs
1 parent 7e37cd2 commit c12a71c

File tree

8 files changed

+126
-9
lines changed

8 files changed

+126
-9
lines changed

CHANGELOG.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,20 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
This project adheres to [Semantic Versioning](https://semver.org) and follows the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## [1.1.0] - 2025-08-07
8+
9+
### Added
10+
11+
- **Fix for ES6 default export issues**: The plugin now corrects wrong names and structures for exported classes, functions, object literals, and their members.
12+
- Fixes the issue where default exports appear as `exports` instead of their actual names
13+
- Corrects class method scoping and memberof relationships
14+
- Extracts meaningful names from `@type` annotations for object literals
15+
- Properly links class members (constructor, properties, methods) to their parent class
16+
- Resolves [JSDoc Issue #1132](https://github.com/jsdoc/jsdoc/issues/1132)
17+
- Resolves [JSDoc Issue #2023](https://github.com/jsdoc/jsdoc/issues/2023)
18+
- Resolves [JSDoc Issue #2038](https://github.com/jsdoc/jsdoc/issues/2038)
619

720
---
821

README.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# JSDoc ESNext Plugin
55

6-
**Smart JSDoc plugin that adds full ES2022+ class-feature support – private fields, static members and arrow-bound methods.**
6+
**JSDoc plugin that adds full ES2022+ class-feature support – private fields, static members, arrow-bound methods, as well as ES6 default export fixes.**
77

88
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.
99

@@ -14,6 +14,7 @@ Modern JavaScript syntax isn’t always accurately recognized by JSDoc. This plu
1414
* Detects `static` class members and applies `@static` and `scope: static`.
1515
* Treats arrow-bound methods as `@function`.
1616
* Detects assignments like `this.#foo = ...` in constructors.
17+
* **Fixes various ES6 default export issues** - corrects wrong names and structures for exported classes, functions, object literals, and their members.
1718
* Works seamlessly with JSDoc's default parser.
1819
* Works with all themes (but see note below!).
1920
* Perfectly integrates with [VisionTheme](https://github.com/alphanull/jsdoc-vision-theme) for a modern UI (optional).
@@ -35,7 +36,7 @@ Then, add the plugin to your JSDoc configuration file:
3536
}
3637
```
3738

38-
## Example
39+
## ES2022 Example
3940

4041
```js
4142
export default class Example {
@@ -73,9 +74,9 @@ export default class Example {
7374
}
7475
```
7576

76-
### Resulting Documentation:
77+
### Resulting Documentation
7778

78-
| ![before](assets/before.jpg) | ![after](assets/after.jpg) |
79+
| ![before](assets/private-before.jpg) | ![after](assets/private-after.jpg) |
7980
| :----------------------------------------------------------: | :----------------------------------------------------------: |
8081
| **Without plugin** | **Using jsdoc-plugin-esnext** |
8182

@@ -84,6 +85,37 @@ export default class Example {
8485
* `#handleClick` is listed as a private function.
8586
* `#compute()` is listed as a private method.
8687

88+
## Exports Example
89+
90+
```js
91+
/**
92+
* Exported as default class.
93+
*/
94+
export default class FooClass {
95+
/**
96+
* FooClass constructor.
97+
* @param {string} message - Additional message.
98+
*/
99+
constructor(message) { }
100+
/**
101+
* This is a class method.
102+
* @param {any} someArg - An arg you need to pass.
103+
*/
104+
classMethod(someArg) {
105+
this.fooAClassMethod(this.message);
106+
}
107+
}
108+
```
109+
110+
### Resulting Documentation
111+
112+
| ![before](assets/exports-before.jpg) | ![after](assets/exports-after.jpg) |
113+
| :----------------------------------------------------------: | :----------------------------------------------------------: |
114+
| **Without plugin** | **Using jsdoc-plugin-esnext** |
115+
116+
* `FooClass` is named correctly (not `exports`).
117+
* Class members (constructor, properties, methods) are correctly linked to their parent class and do not appear in `Global`.
118+
87119
## Limitations
88120

89121
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.

assets/exports-after.jpg

71.2 KB
Loading

assets/exports-before.jpg

56.4 KB
Loading

index.js

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,49 @@ export const handlers = {
1313
/**
1414
* Triggered when a symbol (e.g. Method or field) is found during AST traversal.
1515
* Used to recover names for private class members and inline assignments to `this.#foo`.
16+
* Also fixes ES6 default export names during parsing.
1617
* @param {Object} e The event containing symbol data and AST node information.
1718
*/
1819
symbolFound(e) {
1920

20-
// Phase 1: Detect native private methods defined via #method() syntax
21+
// Phase 1: Fix ES6 default export names during parsing
22+
if (e.code?.name === 'module.exports' && (e.code?.type === 'ClassDeclaration' || e.code?.type === 'FunctionDeclaration' || e.code?.type === 'ObjectExpression')) {
23+
// Check if this is a default export by looking at the parent node
24+
if (e.astnode?.parent?.type === 'ExportDefaultDeclaration') {
25+
const exportName = e.astnode.parent.declaration.id?.name;
26+
if (exportName) {
27+
e.code.name = exportName;
28+
} else if (e.code?.type === 'FunctionDeclaration' && !e.astnode.parent.declaration.id) {
29+
// For anonymous functions, generate a descriptive name
30+
e.code.name = 'anonymousFunction';
31+
} else if (e.code?.type === 'ObjectExpression') {
32+
// For object literals, try to extract name from JSDoc @type annotation
33+
// This will be handled in parseComplete
34+
e.code.name = 'defaultExport';
35+
}
36+
}
37+
}
38+
39+
// Phase 1.5: Fix class member relationships during parsing
40+
if (e.code?.type === 'MethodDefinition' && e.astnode?.parent?.type === 'ClassBody') {
41+
// This is a class method, ensure it has the correct parent reference
42+
const parentClass = e.astnode.parent.parent;
43+
if (parentClass?.type === 'ClassDeclaration' && parentClass.id?.name) {
44+
// The method should be linked to the class name, not module.exports
45+
e.code.memberof = parentClass.id.name;
46+
e.code.scope = 'instance';
47+
}
48+
}
49+
50+
// Phase 2: Detect native private methods defined via #method() syntax
2151
if (e.code?.type === 'MethodDefinition'
2252
&& e.code?.node?.key?.type === 'PrivateName'
2353
&& e.code?.node?.key?.id?.name) {
2454

2555
e.code.name = e.code.node.key.id.name;
2656
}
2757

28-
// Phase 2: Detect assignments to this.#property inside constructor or methods
58+
// Phase 3: Detect assignments to this.#property inside constructor or methods
2959
if (e.code?.name === 'this.' // <- smells like a jsdoc parse bug
3060
&& e.astnode?.type === 'AssignmentExpression'
3161
&& e.astnode?.left?.type === 'MemberExpression'
@@ -105,5 +135,47 @@ export const handlers = {
105135
doclet.scope = 'static';
106136
}
107137
}
138+
139+
// Phase 3: Fix default export names in doclets (simple approach)
140+
for (const doclet of doclets) {
141+
// Fix default export function names
142+
if (doclet.kind === 'function' && doclet.name === 'exports' && doclet.memberof === 'module') {
143+
// Find the actual function name from the doclets
144+
const funcDoclet = doclets.find(d => d.kind === 'function' && d.name !== 'exports' && d.scope === 'global');
145+
if (funcDoclet) {
146+
doclet.name = funcDoclet.name;
147+
doclet.longname = funcDoclet.name;
148+
doclet.memberof = undefined;
149+
}
150+
}
151+
152+
// Fix default export object literals
153+
if (doclet.kind === 'member' && doclet.name === 'exports' && doclet.memberof === 'module' &&
154+
doclet.meta?.code?.type === 'ObjectExpression') {
155+
// Try to extract name from JSDoc @type annotation
156+
const typeMatch = doclet.description?.match(/@type\s*\{([^}]+)\}/);
157+
const objectName = typeMatch?.[1]?.trim() ||
158+
doclet.meta?.code?.node?.properties?.[0]?.key?.name + 'Config' ||
159+
'defaultExport';
160+
161+
doclet.name = objectName;
162+
doclet.longname = objectName;
163+
doclet.memberof = undefined;
164+
}
165+
166+
// Fix class methods that got marked as global
167+
if (doclet.kind === 'function' && doclet.scope === 'global' &&
168+
doclet.meta?.code?.node?.type === 'MethodDefinition') {
169+
// Find the class name
170+
const classDoclet = doclets.find(d => d.kind === 'class' && d.name !== 'exports');
171+
if (classDoclet) {
172+
doclet.scope = 'instance';
173+
doclet.memberof = classDoclet.name;
174+
doclet.longname = `${classDoclet.name}#${doclet.name}`;
175+
doclet.id = doclet.longname; // Also fix the id to ensure proper HTML linking
176+
}
177+
}
178+
}
179+
108180
}
109181
};

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "@alphanull/jsdoc-plugin-esnext",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"license": "MIT",
5-
"description": "Smart JSDoc plugin that adds full ES2022+ class-feature support – private fields, static members and arrow-bound methods.",
5+
"description": "JSDoc plugin that adds full ES2022+ class-feature support – private fields, static members, arrow-bound methods, and ES6 default export fixes.",
66
"keywords": [
77
"jsdoc",
88
"plugin",

0 commit comments

Comments
 (0)