@blumintinc/eslint-plugin-blumint 1.20.176 → 1.20.177
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/lib/index.js +1 -1
- package/lib/rules/jsdoc-above-field.js +57 -31
- package/package.json +1 -1
- package/release-manifest.json +14 -0
package/lib/index.js
CHANGED
|
@@ -108,22 +108,62 @@ exports.jsdocAboveField = (0, createRule_1.createRule)({
|
|
|
108
108
|
: node.range[1],
|
|
109
109
|
};
|
|
110
110
|
};
|
|
111
|
+
const containerOf = (node) => {
|
|
112
|
+
const parent = node.parent;
|
|
113
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
|
|
114
|
+
return { node: parent, members: parent.members, separator: ';' };
|
|
115
|
+
}
|
|
116
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.TSInterfaceBody) {
|
|
117
|
+
return { node: parent, members: parent.body, separator: ';' };
|
|
118
|
+
}
|
|
119
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.ClassBody) {
|
|
120
|
+
return { node: parent, members: parent.body, separator: ';' };
|
|
121
|
+
}
|
|
122
|
+
if (parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
123
|
+
return { node: parent, members: parent.properties, separator: ',' };
|
|
124
|
+
}
|
|
125
|
+
return undefined;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Whether nothing but the container's closing brace follows the block, so
|
|
129
|
+
* the field it trails is the only member it can belong to.
|
|
130
|
+
*/
|
|
131
|
+
const closesContainer = (node, comment) => {
|
|
132
|
+
const container = containerOf(node);
|
|
133
|
+
if (!container) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const closeBrace = sourceCode.getLastToken(container.node);
|
|
137
|
+
if (!closeBrace || comment.range[1] > closeBrace.range[0]) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return !container.members.some((member) => member.range[0] >= comment.range[1]);
|
|
141
|
+
};
|
|
111
142
|
/**
|
|
112
143
|
* Attaches a trailing JSDoc block by token order rather than by line.
|
|
113
144
|
*
|
|
114
|
-
* Prettier
|
|
115
|
-
* line
|
|
116
|
-
* `timeout: number;`
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
145
|
+
* Prettier is not idempotent on a multi-line block that trails a field. One
|
|
146
|
+
* pass reflows it onto its own line ahead of the member's separator, so the
|
|
147
|
+
* block that followed `timeout: number;` sits between `timeout: number` and
|
|
148
|
+
* its `;`; the next pass moves the separator back in front of it, and that
|
|
149
|
+
* separator-first spelling is the fixed point formatted source converges
|
|
150
|
+
* to. Both intermediates and the fixed point document the field above them,
|
|
151
|
+
* so keying on the comment sharing the field's line — or on the separator
|
|
152
|
+
* still following it — leaves the rule inert on the shape it exists to
|
|
153
|
+
* police.
|
|
154
|
+
*
|
|
155
|
+
* Past the separator the member has ended and position alone stops naming
|
|
156
|
+
* an owner: an own-line block there reads as the leading documentation of
|
|
157
|
+
* the next field. That reading needs a next field, so it is unavailable on
|
|
158
|
+
* the last member of a container, where the preceding field is the only
|
|
159
|
+
* candidate left.
|
|
120
160
|
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
161
|
+
* A blank line is the one signal that survives the round trip intact:
|
|
162
|
+
* prettier preserves an authored one and never inserts one while reflowing,
|
|
163
|
+
* so a block held off by an empty line is a deliberate note about the
|
|
164
|
+
* enclosing shape rather than displaced documentation.
|
|
125
165
|
*/
|
|
126
|
-
const trailingJSDocFor = (span) => {
|
|
166
|
+
const trailingJSDocFor = (node, span) => {
|
|
127
167
|
return allComments.find((comment) => {
|
|
128
168
|
if (!isJSDocBlock(comment)) {
|
|
129
169
|
return false;
|
|
@@ -131,11 +171,13 @@ exports.jsdocAboveField = (0, createRule_1.createRule)({
|
|
|
131
171
|
if (comment.range[0] < span.offset) {
|
|
132
172
|
return false;
|
|
133
173
|
}
|
|
174
|
+
const between = sourceCode.text.slice(span.offset, comment.range[0]);
|
|
134
175
|
const precedesSeparator = comment.range[1] <= span.separatorEnd;
|
|
135
176
|
if (!precedesSeparator && comment.loc.start.line !== span.line) {
|
|
136
|
-
|
|
177
|
+
if (!closesContainer(node, comment) || /\n[^\S\n]*\n/.test(between)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
137
180
|
}
|
|
138
|
-
const between = sourceCode.text.slice(span.offset, comment.range[0]);
|
|
139
181
|
return /^[\s;,]*$/.test(between);
|
|
140
182
|
});
|
|
141
183
|
};
|
|
@@ -236,22 +278,6 @@ exports.jsdocAboveField = (0, createRule_1.createRule)({
|
|
|
236
278
|
*/
|
|
237
279
|
const lineIndentOf = (node) => /^[ \t]*/.exec(sourceCode.lines[node.loc.start.line - 1] ?? '')?.[0] ??
|
|
238
280
|
'';
|
|
239
|
-
const containerOf = (node) => {
|
|
240
|
-
const parent = node.parent;
|
|
241
|
-
if (parent?.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
|
|
242
|
-
return { node: parent, members: parent.members, separator: ';' };
|
|
243
|
-
}
|
|
244
|
-
if (parent?.type === utils_1.AST_NODE_TYPES.TSInterfaceBody) {
|
|
245
|
-
return { node: parent, members: parent.body, separator: ';' };
|
|
246
|
-
}
|
|
247
|
-
if (parent?.type === utils_1.AST_NODE_TYPES.ClassBody) {
|
|
248
|
-
return { node: parent, members: parent.body, separator: ';' };
|
|
249
|
-
}
|
|
250
|
-
if (parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
251
|
-
return { node: parent, members: parent.properties, separator: ',' };
|
|
252
|
-
}
|
|
253
|
-
return undefined;
|
|
254
|
-
};
|
|
255
281
|
/**
|
|
256
282
|
* A member's separator sits inside its range for type and class members
|
|
257
283
|
* but outside it for object literal properties, so both spellings have to
|
|
@@ -314,7 +340,7 @@ exports.jsdocAboveField = (0, createRule_1.createRule)({
|
|
|
314
340
|
jsdoc: member === node
|
|
315
341
|
? comment
|
|
316
342
|
: isRelevantNode(member)
|
|
317
|
-
? trailingJSDocFor(fieldSpanOf(member))
|
|
343
|
+
? trailingJSDocFor(member, fieldSpanOf(member))
|
|
318
344
|
: undefined,
|
|
319
345
|
};
|
|
320
346
|
});
|
|
@@ -420,7 +446,7 @@ exports.jsdocAboveField = (0, createRule_1.createRule)({
|
|
|
420
446
|
return;
|
|
421
447
|
}
|
|
422
448
|
const span = fieldSpanOf(node);
|
|
423
|
-
const jsdocComment = trailingJSDocFor(span);
|
|
449
|
+
const jsdocComment = trailingJSDocFor(node, span);
|
|
424
450
|
if (!jsdocComment) {
|
|
425
451
|
return;
|
|
426
452
|
}
|
package/package.json
CHANGED
package/release-manifest.json
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.20.177",
|
|
4
|
+
"date": "2026-08-26T16:36:01.459Z",
|
|
5
|
+
"rules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "jsdoc-above-field",
|
|
8
|
+
"changeType": "fix",
|
|
9
|
+
"issues": [
|
|
10
|
+
2145
|
|
11
|
+
],
|
|
12
|
+
"summary": "claim a trailing block that closes its container (closes #2145)"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
2
16
|
{
|
|
3
17
|
"version": "1.20.176",
|
|
4
18
|
"date": "2026-08-26T11:56:32.903Z",
|