Skip to content

Commit b87cf0d

Browse files
committed
feat: new option sortPropsAlphabetically
1 parent 5924cd7 commit b87cf0d

File tree

5 files changed

+21
-2
lines changed

5 files changed

+21
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ You can use all of the following options with standalone version on <redoc> tag
215215
* `hideHostname` - if set, the protocol and hostname is not shown in the operation definition.
216216
* `expandResponses` - specify which responses to expand by default by response codes. Values should be passed as comma-separated list without spaces e.g. `expandResponses="200,201"`. Special value `"all"` expands all responses by default. Be careful: this option can slow-down documentation rendering time.
217217
* `requiredPropsFirst` - show required properties first ordered in the same order as in `required` array.
218+
* `sortPropsAlphabetically` - sort properties alphabetically
218219
* `showExtensions` - show vendor extensions ("x-" fields). Extensions used by ReDoc are ignored. Can be boolean or an array of `string` with names of extensions to display
219220
* `noAutoAuth` - do not inject Authentication section automatically
220221
* `pathInMiddlePanel` - show path link and HTTP verb in the middle panel instead of the right one

src/services/RedocNormalizedOptions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface RedocRawOptions {
1010
hideHostname?: boolean | string;
1111
expandResponses?: string | 'all';
1212
requiredPropsFirst?: boolean | string;
13+
sortPropsAlphabetically?: boolean | string;
1314
noAutoAuth?: boolean | string;
1415
nativeScrollbars?: boolean | string;
1516
pathInMiddlePanel?: boolean | string;
@@ -109,6 +110,7 @@ export class RedocNormalizedOptions {
109110
hideHostname: boolean;
110111
expandResponses: { [code: string]: boolean } | 'all';
111112
requiredPropsFirst: boolean;
113+
sortPropsAlphabetically: boolean;
112114
noAutoAuth: boolean;
113115
nativeScrollbars: boolean;
114116
pathInMiddlePanel: boolean;
@@ -135,6 +137,7 @@ export class RedocNormalizedOptions {
135137
this.hideHostname = RedocNormalizedOptions.normalizeHideHostname(raw.hideHostname);
136138
this.expandResponses = RedocNormalizedOptions.normalizeExpandResponses(raw.expandResponses);
137139
this.requiredPropsFirst = argValueToBoolean(raw.requiredPropsFirst);
140+
this.sortPropsAlphabetically = argValueToBoolean(raw.sortPropsAlphabetically);
138141
this.noAutoAuth = argValueToBoolean(raw.noAutoAuth);
139142
this.nativeScrollbars = argValueToBoolean(raw.nativeScrollbars);
140143
this.pathInMiddlePanel = argValueToBoolean(raw.pathInMiddlePanel);

src/services/models/Operation.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
memoize,
2121
mergeParams,
2222
normalizeServers,
23+
sortByField,
2324
sortByRequired,
2425
} from '../../utils';
2526
import { ContentItemModel, ExtendedOpenAPIOperation } from '../MenuBuilder';
@@ -152,6 +153,9 @@ export class OperationModel implements IMenuItem {
152153
// TODO: fix pointer
153154
).map(paramOrRef => new FieldModel(this.parser, paramOrRef, this.pointer, this.options));
154155

156+
if (this.options.sortPropsAlphabetically) {
157+
sortByField(_parameters, 'name');
158+
}
155159
if (this.options.requiredPropsFirst) {
156160
sortByRequired(_parameters);
157161
}

src/services/models/Schema.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
isNamedDefinition,
1515
isPrimitiveType,
1616
JsonPointer,
17+
sortByField,
1718
sortByRequired,
1819
} from '../../utils/';
1920

@@ -261,8 +262,12 @@ function buildFields(
261262
);
262263
});
263264

265+
if (options.sortPropsAlphabetically) {
266+
sortByField(fields, 'name');
267+
}
264268
if (options.requiredPropsFirst) {
265-
sortByRequired(fields, schema.required);
269+
// if not sort alphabetically sort in the order from required keyword
270+
sortByRequired(fields, !options.sortPropsAlphabetically ? schema.required : undefined);
266271
}
267272

268273
if (typeof additionalProps === 'object' || additionalProps === true) {

src/utils/openapi.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,19 @@ export function sortByRequired(
196196
} else if (a.required && !b.required) {
197197
return -1;
198198
} else if (a.required && b.required) {
199-
return order.indexOf(a.name) > order.indexOf(b.name) ? 1 : -1;
199+
return order.indexOf(a.name) - order.indexOf(b.name);
200200
} else {
201201
return 0;
202202
}
203203
});
204204
}
205205

206+
export function sortByField<T extends string>(fields: Array<{ [P in T]: string }>, param: T) {
207+
fields.sort((a, b) => {
208+
return a[param].localeCompare(b[param]);
209+
});
210+
}
211+
206212
export function mergeParams(
207213
parser: OpenAPIParser,
208214
pathParams: Array<Referenced<OpenAPIParameter>> = [],

0 commit comments

Comments
 (0)