Skip to content

Commit 8ace124

Browse files
Fix sort-prop-types trailing comments
1 parent c99d3b2 commit 8ace124

2 files changed

Lines changed: 96 additions & 6 deletions

File tree

lib/util/propTypesSort.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,19 @@ function fixPropTypesSort(
150150
function sortInSource(allNodes, source) {
151151
const originalSource = source;
152152
const sourceCode = getSourceCode(context);
153+
const trailingCommentRanges = new Set();
154+
153155
for (let i = 0; i < allNodes.length; i++) {
154156
const node = allNodes[i];
155157
let commentAfter = [];
156158
let commentBefore = [];
157159
let newStart = 0;
158160
let newEnd = 0;
159161
try {
160-
commentBefore = sourceCode.getCommentsBefore(node);
162+
commentBefore = sourceCode.getCommentsBefore(node)
163+
.filter((comment) => !trailingCommentRanges.has(comment.range.join(':')));
161164
commentAfter = sourceCode.getCommentsAfter(node);
165+
commentAfter.forEach((comment) => trailingCommentRanges.add(comment.range.join(':')));
162166
} catch (e) { /**/ }
163167

164168
if (commentAfter.length === 0 || commentBefore.length === 0) {
@@ -197,10 +201,7 @@ function fixPropTypesSort(
197201
const sortedAttr = sortedAttributes[index];
198202
const commentNode = commentnodeMap.get(sortedAttr);
199203
let sortedAttrText = sourceCodeText.slice(commentNode.start, commentNode.end);
200-
const sortedAttrTextLastChar = sortedAttrText[sortedAttrText.length - 1];
201-
if (!separator && [';', ','].some((allowedSep) => sortedAttrTextLastChar === allowedSep)) {
202-
separator = sortedAttrTextLastChar;
203-
}
204+
let sortedAttrTextStart = commentNode.start;
204205
if (sortShapeProp && isShapeProp(sortedAttr.value)) {
205206
const shape = getShapeProperties(sortedAttr.value);
206207
if (shape) {
@@ -209,9 +210,20 @@ function fixPropTypesSort(
209210
originalSource
210211
);
211212
sortedAttrText = attrSource.slice(sortedAttr.range[0], sortedAttr.range[1]);
213+
sortedAttrTextStart = sortedAttr.range[0];
212214
}
213215
}
214-
const sortedAttrTextVal = checkTypes && !sortedAttrText.endsWith(separator) ? `${sortedAttrText}${separator}` : sortedAttrText;
216+
const trailingComment = sourceCode.getCommentsAfter(sortedAttr)[0];
217+
const separatorIndex = trailingComment ? trailingComment.range[0] - sortedAttrTextStart : sortedAttrText.length;
218+
const sortedAttrTextLastChar = sortedAttrText.slice(0, separatorIndex).trim().slice(-1);
219+
if (!separator && [';', ','].some((allowedSep) => sortedAttrTextLastChar === allowedSep)) {
220+
separator = sortedAttrTextLastChar;
221+
}
222+
const hasSeparator = sortedAttrText.slice(0, separatorIndex).trim().endsWith(separator);
223+
const needsSeparator = checkTypes && separator && !hasSeparator;
224+
const sortedAttrTextVal = needsSeparator
225+
? `${sortedAttrText.slice(0, separatorIndex)}${separator}${sortedAttrText.slice(separatorIndex)}`
226+
: sortedAttrText;
215227
return `${acc.slice(0, commentnodeMap.get(attr).start)}${sortedAttrTextVal}${acc.slice(commentnodeMap.get(attr).end)}`;
216228
}, source);
217229
});

tests/lib/rules/sort-prop-types.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2313,6 +2313,84 @@ ruleTester.run('sort-prop-types', rule, {
23132313
},
23142314
],
23152315
},
2316+
{
2317+
code: `
2318+
type Props = {
2319+
onClose: () => void; // closes the dialog
2320+
onSave?: () => void; // saves the dialog
2321+
id: string; // identifies the dialog
2322+
};
2323+
function Dialog(props: Props) {
2324+
return <div />;
2325+
}
2326+
`,
2327+
output: `
2328+
type Props = {
2329+
id: string; // identifies the dialog
2330+
onClose: () => void; // closes the dialog
2331+
onSave?: () => void; // saves the dialog
2332+
};
2333+
function Dialog(props: Props) {
2334+
return <div />;
2335+
}
2336+
`,
2337+
parser: parsers.TYPESCRIPT_ESLINT,
2338+
options: [{
2339+
callbacksLast: true,
2340+
noSortAlphabetically: true,
2341+
checkTypes: true,
2342+
}],
2343+
errors: [
2344+
{
2345+
messageId: 'callbackPropsLast',
2346+
},
2347+
],
2348+
},
2349+
{
2350+
code: `
2351+
type Props = {
2352+
onClose: () => void;
2353+
onSave?: () => void;
2354+
initialContractInfo?: ContractInfo; // used to pre-populate the form just for our tests
2355+
contractVersionTraceId?: TraceId; // used when editing an existing contract
2356+
contractContainerId: TraceId;
2357+
wizardStartIndex?: number;
2358+
contractStatus?: BackendContractStatus;
2359+
contractVersion?: BackendContractVersion;
2360+
};
2361+
function ContractVersionWizard(props: Props) {
2362+
return <div />;
2363+
}
2364+
`,
2365+
output: `
2366+
type Props = {
2367+
initialContractInfo?: ContractInfo; // used to pre-populate the form just for our tests
2368+
contractVersionTraceId?: TraceId; // used when editing an existing contract
2369+
contractContainerId: TraceId;
2370+
wizardStartIndex?: number;
2371+
contractStatus?: BackendContractStatus;
2372+
contractVersion?: BackendContractVersion;
2373+
onClose: () => void;
2374+
onSave?: () => void;
2375+
};
2376+
function ContractVersionWizard(props: Props) {
2377+
return <div />;
2378+
}
2379+
`,
2380+
parser: parsers.TYPESCRIPT_ESLINT,
2381+
options: [{
2382+
callbacksLast: true,
2383+
requiredFirst: true,
2384+
sortShapeProp: true,
2385+
noSortAlphabetically: true,
2386+
checkTypes: true,
2387+
}],
2388+
errors: [
2389+
{
2390+
messageId: 'callbackPropsLast',
2391+
},
2392+
],
2393+
},
23162394
{
23172395
code: `
23182396
type Props = {

0 commit comments

Comments
 (0)