@qubit-ltd/jsdoc-theme 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -6
- package/package.json +1 -1
- package/tmpl/container.tmpl +92 -7
- package/tmpl/properties.tmpl +120 -97
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# qubit-jsdoc-theme
|
|
2
2
|
|
|
3
|
-
[](https://github.com/Haixing-Hu/qubit-jsdoc-theme) [](https://github.com/Haixing-Hu/qubit-jsdoc-theme/fork) ](https://github.com/Haixing-Hu/qubit-jsdoc-theme) [](https://github.com/Haixing-Hu/qubit-jsdoc-theme/fork)  [](https://github.com/Haixing-Hu/qubit-jsdoc-theme/issues) [](https://github.com/Haixing-Hu/qubit-jsdoc-theme/graphs/contributors) [](https://github.com/Haixing-Hu/qubit-jsdoc-theme/blob/master/LICENSE)
|
|
4
4
|
<br>
|
|
5
5
|
|
|
6
6
|
**Based on [clean-jsdoc-theme](https://github.com/ankitskvmdam/clean-jsdoc-theme) v4.3.0**
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
## Enhanced Features (New in qubit-jsdoc-theme)
|
|
11
11
|
|
|
12
12
|
- **🆕 Smart Properties Table:** Automatically generates a dedicated "Properties" section for classes, displaying all class properties in a clean table format with type information and descriptions derived from individual field JSDoc comments.
|
|
13
|
+
- **🆕 Static/Instance Property Separation:** Intelligently separates static and instance properties into distinct sections, with automatic scope detection and fallback logic for accurate classification.
|
|
14
|
+
- **🆕 Getter/Setter Pair Merging:** Automatically detects and merges getter/setter pairs into single table entries, displaying combined descriptions and access type indicators.
|
|
13
15
|
- **🆕 Intelligent Constructor Detection:** Only displays the "Constructor" section when a class has explicit constructor documentation, avoiding empty constructor sections for classes without custom constructors.
|
|
14
16
|
- **🆕 Field-Level JSDoc Support:** Properties table is populated directly from `@type` annotations on individual class fields, eliminating the need for redundant `@property` tags in class-level JSDoc.
|
|
15
17
|
- **🆕 Clean Member Organization:** Properties are displayed in their own dedicated section, while the "Members" section can be configured to show only methods, avoiding duplication.
|
|
@@ -33,8 +35,10 @@
|
|
|
33
35
|
The enhanced features of `qubit-jsdoc-theme` include:
|
|
34
36
|
|
|
35
37
|
1. **Smart Properties Table**: Automatically generated from field-level `@type` annotations
|
|
36
|
-
2. **
|
|
37
|
-
3. **
|
|
38
|
+
2. **Static/Instance Property Separation**: Properties are automatically categorized and displayed in separate sections
|
|
39
|
+
3. **Getter/Setter Pair Merging**: Related accessor methods are intelligently combined in the properties table
|
|
40
|
+
4. **Intelligent Constructor Detection**: Only shows constructor section when explicitly documented
|
|
41
|
+
5. **Clean Organization**: Properties and methods are clearly separated with enhanced categorization
|
|
38
42
|
|
|
39
43
|
For the base theme features and styling, you can reference the original [clean-jsdoc-theme demo](https://ankdev.me/clean-jsdoc-theme/v4).
|
|
40
44
|
|
|
@@ -97,6 +101,13 @@ Here's how to document your JavaScript classes to take advantage of the enhanced
|
|
|
97
101
|
* @author John Doe
|
|
98
102
|
*/
|
|
99
103
|
class User {
|
|
104
|
+
/**
|
|
105
|
+
* Total number of users created
|
|
106
|
+
* @type {number}
|
|
107
|
+
* @static
|
|
108
|
+
*/
|
|
109
|
+
static userCount = 0;
|
|
110
|
+
|
|
100
111
|
/**
|
|
101
112
|
* User's unique identifier
|
|
102
113
|
* @type {string}
|
|
@@ -121,6 +132,24 @@ class User {
|
|
|
121
132
|
*/
|
|
122
133
|
isActive;
|
|
123
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Get user's full display information
|
|
137
|
+
* @type {string}
|
|
138
|
+
*/
|
|
139
|
+
get displayInfo() {
|
|
140
|
+
return `${this.name} (${this.email})`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Set user's full display information
|
|
145
|
+
* @type {string}
|
|
146
|
+
*/
|
|
147
|
+
set displayInfo(value) {
|
|
148
|
+
const parts = value.split(' (');
|
|
149
|
+
this.name = parts[0];
|
|
150
|
+
this.email = parts[1]?.replace(')', '') || '';
|
|
151
|
+
}
|
|
152
|
+
|
|
124
153
|
/**
|
|
125
154
|
* Creates a new user instance
|
|
126
155
|
* @param {string} name - The user's name
|
|
@@ -130,6 +159,7 @@ class User {
|
|
|
130
159
|
this.name = name;
|
|
131
160
|
this.email = email;
|
|
132
161
|
this.isActive = true;
|
|
162
|
+
User.userCount++;
|
|
133
163
|
}
|
|
134
164
|
|
|
135
165
|
/**
|
|
@@ -144,9 +174,10 @@ class User {
|
|
|
144
174
|
```
|
|
145
175
|
|
|
146
176
|
This will generate documentation with:
|
|
147
|
-
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
177
|
+
- **Static Properties section**: Showing `userCount` and other static properties
|
|
178
|
+
- **Instance Properties section**: Showing `id`, `name`, `email`, `isActive` and merged `displayInfo` (getter/setter)
|
|
179
|
+
- **Constructor section**: Only displayed when constructor has documentation
|
|
180
|
+
- **Members section**: For methods like `activate()` (properties are excluded to avoid duplication)
|
|
150
181
|
|
|
151
182
|
## Example JSDoc Config
|
|
152
183
|
|
|
@@ -706,6 +737,26 @@ Don't forget to add the following in your jsdoc config file, otherwise toc will
|
|
|
706
737
|
|
|
707
738
|
## Changelog
|
|
708
739
|
|
|
740
|
+
### v1.5.0 (2025-01-XX)
|
|
741
|
+
|
|
742
|
+
**Advanced Property Management & Enhanced Documentation Features**
|
|
743
|
+
|
|
744
|
+
#### 🔧 New Features
|
|
745
|
+
- **Static/Instance Property Separation**: Automatically separates static and instance properties into distinct sections with intelligent scope detection
|
|
746
|
+
- **Getter/Setter Pair Merging**: Smart detection and merging of getter/setter pairs into unified table entries with combined descriptions
|
|
747
|
+
- **Enhanced Scope Detection**: Improved logic for detecting property scope using JSDoc's built-in detection with fallback heuristics
|
|
748
|
+
- **Access Type Indicators**: Clear visual indicators for property access types (property, getter, setter, getter/setter)
|
|
749
|
+
|
|
750
|
+
#### 🛠️ Technical Improvements
|
|
751
|
+
- **Advanced Template Logic**: Enhanced `container.tmpl` with sophisticated property categorization and merging algorithms
|
|
752
|
+
- **Improved Properties Template**: Updated `properties.tmpl` to support separate rendering of static and instance properties
|
|
753
|
+
- **Fallback Scope Detection**: Added longname pattern analysis for accurate scope detection when JSDoc's built-in detection fails
|
|
754
|
+
- **Bilingual Section Headers**: Support for both English and Chinese section headers in property tables
|
|
755
|
+
|
|
756
|
+
#### 📖 Documentation Updates
|
|
757
|
+
- **Enhanced Usage Examples**: Updated examples to demonstrate static properties and getter/setter documentation
|
|
758
|
+
- **Feature Documentation**: Comprehensive documentation for new property management features
|
|
759
|
+
|
|
709
760
|
### v1.4.0 (2025-01-XX)
|
|
710
761
|
|
|
711
762
|
**Enhanced Mixin Documentation Support**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qubit-ltd/jsdoc-theme",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A customized JSDoc theme based on clean-jsdoc-theme, enhanced with properties table and constructor detection features for Qubit projects.",
|
|
5
5
|
"main": "publish.js",
|
|
6
6
|
"author": "Haixing Hu (starfish.hu@gmail.com)",
|
package/tmpl/container.tmpl
CHANGED
|
@@ -152,18 +152,103 @@
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
if (allMembers && allMembers.length && allMembers.forEach) {
|
|
155
|
-
//
|
|
156
|
-
var
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
// Separate static and instance properties, merge getter/setter pairs
|
|
156
|
+
var staticProperties = {};
|
|
157
|
+
var instanceProperties = {};
|
|
158
|
+
|
|
159
|
+
allMembers.forEach(function(member) {
|
|
160
|
+
// Use JSDoc's built-in scope detection with fallback logic
|
|
161
|
+
// JSDoc sometimes misidentifies scope in mixin classes, so we add some heuristics
|
|
162
|
+
var isStatic = member.scope === 'static';
|
|
163
|
+
|
|
164
|
+
// Fallback: check longname pattern for static members (uses '.' separator)
|
|
165
|
+
// and instance members (uses '#' separator)
|
|
166
|
+
if (member.longname) {
|
|
167
|
+
var hasStaticSeparator = member.longname.indexOf('.') > member.longname.lastIndexOf('#');
|
|
168
|
+
var hasInstanceSeparator = member.longname.indexOf('#') > member.longname.lastIndexOf('.');
|
|
169
|
+
|
|
170
|
+
if (hasStaticSeparator && !hasInstanceSeparator) {
|
|
171
|
+
isStatic = true;
|
|
172
|
+
} else if (hasInstanceSeparator && !hasStaticSeparator) {
|
|
173
|
+
isStatic = false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
var targetObj = isStatic ? staticProperties : instanceProperties;
|
|
178
|
+
var propName = member.name;
|
|
179
|
+
|
|
180
|
+
if (!targetObj[propName]) {
|
|
181
|
+
targetObj[propName] = {
|
|
182
|
+
name: propName,
|
|
160
183
|
type: member.type,
|
|
161
184
|
description: member.description || '',
|
|
162
185
|
nullable: member.nullable,
|
|
163
186
|
optional: member.optional,
|
|
164
|
-
defaultvalue: member.defaultvalue
|
|
187
|
+
defaultvalue: member.defaultvalue,
|
|
188
|
+
isStatic: isStatic,
|
|
189
|
+
hasGetter: false,
|
|
190
|
+
hasSetter: false,
|
|
191
|
+
getterDescription: '',
|
|
192
|
+
setterDescription: ''
|
|
165
193
|
};
|
|
166
|
-
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Check if this is a getter or setter based on description or other indicators
|
|
197
|
+
var desc = (member.description || '').toLowerCase();
|
|
198
|
+
if (desc.indexOf('获取') === 0 || desc.indexOf('get') === 0) {
|
|
199
|
+
targetObj[propName].hasGetter = true;
|
|
200
|
+
targetObj[propName].getterDescription = member.description || '';
|
|
201
|
+
} else if (desc.indexOf('设置') === 0 || desc.indexOf('set') === 0) {
|
|
202
|
+
targetObj[propName].hasSetter = true;
|
|
203
|
+
targetObj[propName].setterDescription = member.description || '';
|
|
204
|
+
} else {
|
|
205
|
+
// Regular property, use the description as is
|
|
206
|
+
if (!targetObj[propName].description) {
|
|
207
|
+
targetObj[propName].description = member.description || '';
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Convert objects to arrays
|
|
213
|
+
var staticPropsArray = Object.keys(staticProperties).map(function(key) {
|
|
214
|
+
var prop = staticProperties[key];
|
|
215
|
+
// Combine getter/setter descriptions
|
|
216
|
+
if (prop.hasGetter && prop.hasSetter) {
|
|
217
|
+
prop.description = prop.getterDescription + ' / ' + prop.setterDescription;
|
|
218
|
+
prop.accessType = 'getter/setter';
|
|
219
|
+
} else if (prop.hasGetter) {
|
|
220
|
+
prop.description = prop.getterDescription;
|
|
221
|
+
prop.accessType = 'getter';
|
|
222
|
+
} else if (prop.hasSetter) {
|
|
223
|
+
prop.description = prop.setterDescription;
|
|
224
|
+
prop.accessType = 'setter';
|
|
225
|
+
} else {
|
|
226
|
+
prop.accessType = 'property';
|
|
227
|
+
}
|
|
228
|
+
return prop;
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
var instancePropsArray = Object.keys(instanceProperties).map(function(key) {
|
|
232
|
+
var prop = instanceProperties[key];
|
|
233
|
+
if (prop.hasGetter && prop.hasSetter) {
|
|
234
|
+
prop.description = prop.getterDescription + ' / ' + prop.setterDescription;
|
|
235
|
+
prop.accessType = 'getter/setter';
|
|
236
|
+
} else if (prop.hasGetter) {
|
|
237
|
+
prop.description = prop.getterDescription;
|
|
238
|
+
prop.accessType = 'getter';
|
|
239
|
+
} else if (prop.hasSetter) {
|
|
240
|
+
prop.description = prop.setterDescription;
|
|
241
|
+
prop.accessType = 'setter';
|
|
242
|
+
} else {
|
|
243
|
+
prop.accessType = 'property';
|
|
244
|
+
}
|
|
245
|
+
return prop;
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Create properties data with both static and instance properties
|
|
249
|
+
var propertiesData = {
|
|
250
|
+
staticProperties: staticPropsArray,
|
|
251
|
+
instanceProperties: instancePropsArray
|
|
167
252
|
};
|
|
168
253
|
?>
|
|
169
254
|
<h2 id="properties" class="subsection-title has-anchor"><?js= t('properties') ?></h2>
|
package/tmpl/properties.tmpl
CHANGED
|
@@ -1,109 +1,132 @@
|
|
|
1
1
|
<?js
|
|
2
2
|
var data = obj;
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
var self = this;
|
|
4
|
+
|
|
5
|
+
// Handle both old format (single properties array) and new format (static/instance separation)
|
|
6
|
+
var staticProps = data.staticProperties || [];
|
|
7
|
+
var instanceProps = data.instanceProperties || data.properties || [];
|
|
8
|
+
|
|
9
|
+
// Function to render a properties table
|
|
10
|
+
function renderPropertiesTable(props, title) {
|
|
11
|
+
if (!props || props.length === 0) return '';
|
|
12
|
+
|
|
13
|
+
/* sort subprops under their parent props (like opts.classname) */
|
|
14
|
+
var parentProp = null;
|
|
15
|
+
props.forEach(function(prop, i) {
|
|
16
|
+
if (!prop) { return; }
|
|
17
|
+
if ( parentProp && prop.name && prop.name.indexOf(parentProp.name + '.') === 0 ) {
|
|
18
|
+
prop.name = prop.name.substr(parentProp.name.length+1);
|
|
19
|
+
parentProp.subprops = parentProp.subprops || [];
|
|
20
|
+
parentProp.subprops.push(prop);
|
|
21
|
+
props[i] = null;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
parentProp = prop;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/* determine if we need extra columns, "attributes" and "default" */
|
|
29
|
+
props.hasAttributes = false;
|
|
30
|
+
props.hasDefault = false;
|
|
31
|
+
props.hasName = false;
|
|
32
|
+
|
|
33
|
+
props.forEach(function(prop) {
|
|
34
|
+
if (!prop) { return; }
|
|
35
|
+
|
|
36
|
+
if (prop.optional || prop.nullable) {
|
|
37
|
+
props.hasAttributes = true;
|
|
38
|
+
}
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
props.hasName = false;
|
|
40
|
+
if (prop.name) {
|
|
41
|
+
props.hasName = true;
|
|
42
|
+
}
|
|
24
43
|
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
if (typeof prop.defaultvalue !== 'undefined' && !data.isEnum) {
|
|
45
|
+
props.hasDefault = true;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
27
48
|
|
|
28
|
-
|
|
29
|
-
|
|
49
|
+
var html = '';
|
|
50
|
+
if (title) {
|
|
51
|
+
html += '<h3 class="subsection-title">' + title + '</h3>';
|
|
30
52
|
}
|
|
31
53
|
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
html += '<div class="allow-overflow">';
|
|
55
|
+
html += '<table class="props">';
|
|
56
|
+
html += '<thead><tr>';
|
|
57
|
+
|
|
58
|
+
if (props.hasName) {
|
|
59
|
+
html += '<th>' + t('name') + '</th>';
|
|
34
60
|
}
|
|
61
|
+
html += '<th>' + t('type') + '</th>';
|
|
35
62
|
|
|
36
|
-
if (
|
|
37
|
-
|
|
63
|
+
if (props.hasAttributes) {
|
|
64
|
+
html += '<th>' + t('attributes') + '</th>';
|
|
38
65
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<?js } ?>
|
|
48
|
-
|
|
49
|
-
<th><?js= t('type') ?></th>
|
|
50
|
-
|
|
51
|
-
<?js if (props.hasAttributes) {?>
|
|
52
|
-
<th><?js= t('attributes') ?></th>
|
|
53
|
-
<?js } ?>
|
|
54
|
-
|
|
55
|
-
<?js if (props.hasDefault) {?>
|
|
56
|
-
<th><?js= t('default') ?></th>
|
|
57
|
-
<?js } ?>
|
|
58
|
-
|
|
59
|
-
<th class="last"><?js= t('description') ?></th>
|
|
60
|
-
</tr>
|
|
61
|
-
</thead>
|
|
62
|
-
|
|
63
|
-
<tbody>
|
|
64
|
-
<?js
|
|
65
|
-
var self = this;
|
|
66
|
+
|
|
67
|
+
if (props.hasDefault) {
|
|
68
|
+
html += '<th>' + t('default') + '</th>';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
html += '<th class="last">' + t('description') + '</th>';
|
|
72
|
+
html += '</tr></thead><tbody>';
|
|
73
|
+
|
|
66
74
|
props.forEach(function(prop) {
|
|
67
75
|
if (!prop) { return; }
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
</
|
|
109
|
-
|
|
76
|
+
|
|
77
|
+
html += '<tr>';
|
|
78
|
+
|
|
79
|
+
if (props.hasName) {
|
|
80
|
+
var nameDisplay = prop.name;
|
|
81
|
+
if (prop.accessType && prop.accessType !== 'property') {
|
|
82
|
+
nameDisplay += ' <em>(' + prop.accessType + ')</em>';
|
|
83
|
+
}
|
|
84
|
+
html += '<td class="name"><code>' + nameDisplay + '</code></td>';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
html += '<td class="type">';
|
|
88
|
+
if (prop.type && prop.type.names) {
|
|
89
|
+
html += self.partial('type.tmpl', prop.type.names);
|
|
90
|
+
}
|
|
91
|
+
html += '</td>';
|
|
92
|
+
|
|
93
|
+
if (props.hasAttributes) {
|
|
94
|
+
html += '<td class="attributes">';
|
|
95
|
+
if (prop.optional) {
|
|
96
|
+
html += '<optional><br>';
|
|
97
|
+
}
|
|
98
|
+
if (prop.nullable) {
|
|
99
|
+
html += '<nullable><br>';
|
|
100
|
+
}
|
|
101
|
+
html += '</td>';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (props.hasDefault) {
|
|
105
|
+
html += '<td class="default">';
|
|
106
|
+
if (typeof prop.defaultvalue !== 'undefined') {
|
|
107
|
+
html += self.htmlsafe(prop.defaultvalue);
|
|
108
|
+
}
|
|
109
|
+
html += '</td>';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
html += '<td class="description last">' + (prop.description || '');
|
|
113
|
+
if (prop.subprops) {
|
|
114
|
+
html += '<h6>Properties</h6>' + self.partial('properties.tmpl', prop);
|
|
115
|
+
}
|
|
116
|
+
html += '</td>';
|
|
117
|
+
|
|
118
|
+
html += '</tr>';
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
html += '</tbody></table></div>';
|
|
122
|
+
return html;
|
|
123
|
+
}
|
|
124
|
+
?>
|
|
125
|
+
|
|
126
|
+
<?js if (staticProps.length > 0) { ?>
|
|
127
|
+
<?js= renderPropertiesTable(staticProps, '静态属性') ?>
|
|
128
|
+
<?js } ?>
|
|
129
|
+
|
|
130
|
+
<?js if (instanceProps.length > 0) { ?>
|
|
131
|
+
<?js= renderPropertiesTable(instanceProps, staticProps.length > 0 ? '实例属性' : null) ?>
|
|
132
|
+
<?js } ?>
|